roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4848 <p>
4849 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4850
4851 <p>
4852 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (typeof(name) == 'object') {
9582                 for(var i in name) {
9583                     this.attr(i, name[i]);
9584                 }
9585                 return name;
9586             }
9587             
9588             
9589             if (!this.dom.hasAttribute(name)) {
9590                 return undefined;
9591             }
9592             return this.dom.getAttribute(name);
9593         }
9594         
9595         
9596         
9597     };
9598
9599     var ep = El.prototype;
9600
9601     /**
9602      * Appends an event handler (Shorthand for addListener)
9603      * @param {String}   eventName     The type of event to append
9604      * @param {Function} fn        The method the event invokes
9605      * @param {Object} scope       (optional) The scope (this object) of the fn
9606      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9607      * @method
9608      */
9609     ep.on = ep.addListener;
9610         // backwards compat
9611     ep.mon = ep.addListener;
9612
9613     /**
9614      * Removes an event handler from this element (shorthand for removeListener)
9615      * @param {String} eventName the type of event to remove
9616      * @param {Function} fn the method the event invokes
9617      * @return {Roo.Element} this
9618      * @method
9619      */
9620     ep.un = ep.removeListener;
9621
9622     /**
9623      * true to automatically adjust width and height settings for box-model issues (default to true)
9624      */
9625     ep.autoBoxAdjust = true;
9626
9627     // private
9628     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9629
9630     // private
9631     El.addUnits = function(v, defaultUnit){
9632         if(v === "" || v == "auto"){
9633             return v;
9634         }
9635         if(v === undefined){
9636             return '';
9637         }
9638         if(typeof v == "number" || !El.unitPattern.test(v)){
9639             return v + (defaultUnit || 'px');
9640         }
9641         return v;
9642     };
9643
9644     // special markup used throughout Roo when box wrapping elements
9645     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9646     /**
9647      * Visibility mode constant - Use visibility to hide element
9648      * @static
9649      * @type Number
9650      */
9651     El.VISIBILITY = 1;
9652     /**
9653      * Visibility mode constant - Use display to hide element
9654      * @static
9655      * @type Number
9656      */
9657     El.DISPLAY = 2;
9658
9659     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9660     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9661     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9662
9663
9664
9665     /**
9666      * @private
9667      */
9668     El.cache = {};
9669
9670     var docEl;
9671
9672     /**
9673      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9674      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9675      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9676      * @return {Element} The Element object
9677      * @static
9678      */
9679     El.get = function(el){
9680         var ex, elm, id;
9681         if(!el){ return null; }
9682         if(typeof el == "string"){ // element id
9683             if(!(elm = document.getElementById(el))){
9684                 return null;
9685             }
9686             if(ex = El.cache[el]){
9687                 ex.dom = elm;
9688             }else{
9689                 ex = El.cache[el] = new El(elm);
9690             }
9691             return ex;
9692         }else if(el.tagName){ // dom element
9693             if(!(id = el.id)){
9694                 id = Roo.id(el);
9695             }
9696             if(ex = El.cache[id]){
9697                 ex.dom = el;
9698             }else{
9699                 ex = El.cache[id] = new El(el);
9700             }
9701             return ex;
9702         }else if(el instanceof El){
9703             if(el != docEl){
9704                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9705                                                               // catch case where it hasn't been appended
9706                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9707             }
9708             return el;
9709         }else if(el.isComposite){
9710             return el;
9711         }else if(el instanceof Array){
9712             return El.select(el);
9713         }else if(el == document){
9714             // create a bogus element object representing the document object
9715             if(!docEl){
9716                 var f = function(){};
9717                 f.prototype = El.prototype;
9718                 docEl = new f();
9719                 docEl.dom = document;
9720             }
9721             return docEl;
9722         }
9723         return null;
9724     };
9725
9726     // private
9727     El.uncache = function(el){
9728         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9729             if(a[i]){
9730                 delete El.cache[a[i].id || a[i]];
9731             }
9732         }
9733     };
9734
9735     // private
9736     // Garbage collection - uncache elements/purge listeners on orphaned elements
9737     // so we don't hold a reference and cause the browser to retain them
9738     El.garbageCollect = function(){
9739         if(!Roo.enableGarbageCollector){
9740             clearInterval(El.collectorThread);
9741             return;
9742         }
9743         for(var eid in El.cache){
9744             var el = El.cache[eid], d = el.dom;
9745             // -------------------------------------------------------
9746             // Determining what is garbage:
9747             // -------------------------------------------------------
9748             // !d
9749             // dom node is null, definitely garbage
9750             // -------------------------------------------------------
9751             // !d.parentNode
9752             // no parentNode == direct orphan, definitely garbage
9753             // -------------------------------------------------------
9754             // !d.offsetParent && !document.getElementById(eid)
9755             // display none elements have no offsetParent so we will
9756             // also try to look it up by it's id. However, check
9757             // offsetParent first so we don't do unneeded lookups.
9758             // This enables collection of elements that are not orphans
9759             // directly, but somewhere up the line they have an orphan
9760             // parent.
9761             // -------------------------------------------------------
9762             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9763                 delete El.cache[eid];
9764                 if(d && Roo.enableListenerCollection){
9765                     E.purgeElement(d);
9766                 }
9767             }
9768         }
9769     }
9770     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9771
9772
9773     // dom is optional
9774     El.Flyweight = function(dom){
9775         this.dom = dom;
9776     };
9777     El.Flyweight.prototype = El.prototype;
9778
9779     El._flyweights = {};
9780     /**
9781      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9782      * the dom node can be overwritten by other code.
9783      * @param {String/HTMLElement} el The dom node or id
9784      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9785      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9786      * @static
9787      * @return {Element} The shared Element object
9788      */
9789     El.fly = function(el, named){
9790         named = named || '_global';
9791         el = Roo.getDom(el);
9792         if(!el){
9793             return null;
9794         }
9795         if(!El._flyweights[named]){
9796             El._flyweights[named] = new El.Flyweight();
9797         }
9798         El._flyweights[named].dom = el;
9799         return El._flyweights[named];
9800     };
9801
9802     /**
9803      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9804      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9805      * Shorthand of {@link Roo.Element#get}
9806      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9807      * @return {Element} The Element object
9808      * @member Roo
9809      * @method get
9810      */
9811     Roo.get = El.get;
9812     /**
9813      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9814      * the dom node can be overwritten by other code.
9815      * Shorthand of {@link Roo.Element#fly}
9816      * @param {String/HTMLElement} el The dom node or id
9817      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9818      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9819      * @static
9820      * @return {Element} The shared Element object
9821      * @member Roo
9822      * @method fly
9823      */
9824     Roo.fly = El.fly;
9825
9826     // speedy lookup for elements never to box adjust
9827     var noBoxAdjust = Roo.isStrict ? {
9828         select:1
9829     } : {
9830         input:1, select:1, textarea:1
9831     };
9832     if(Roo.isIE || Roo.isGecko){
9833         noBoxAdjust['button'] = 1;
9834     }
9835
9836
9837     Roo.EventManager.on(window, 'unload', function(){
9838         delete El.cache;
9839         delete El._flyweights;
9840     });
9841 })();
9842
9843
9844
9845
9846 if(Roo.DomQuery){
9847     Roo.Element.selectorFunction = Roo.DomQuery.select;
9848 }
9849
9850 Roo.Element.select = function(selector, unique, root){
9851     var els;
9852     if(typeof selector == "string"){
9853         els = Roo.Element.selectorFunction(selector, root);
9854     }else if(selector.length !== undefined){
9855         els = selector;
9856     }else{
9857         throw "Invalid selector";
9858     }
9859     if(unique === true){
9860         return new Roo.CompositeElement(els);
9861     }else{
9862         return new Roo.CompositeElementLite(els);
9863     }
9864 };
9865 /**
9866  * Selects elements based on the passed CSS selector to enable working on them as 1.
9867  * @param {String/Array} selector The CSS selector or an array of elements
9868  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9869  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9870  * @return {CompositeElementLite/CompositeElement}
9871  * @member Roo
9872  * @method select
9873  */
9874 Roo.select = Roo.Element.select;
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887
9888
9889 /*
9890  * Based on:
9891  * Ext JS Library 1.1.1
9892  * Copyright(c) 2006-2007, Ext JS, LLC.
9893  *
9894  * Originally Released Under LGPL - original licence link has changed is not relivant.
9895  *
9896  * Fork - LGPL
9897  * <script type="text/javascript">
9898  */
9899
9900
9901
9902 //Notifies Element that fx methods are available
9903 Roo.enableFx = true;
9904
9905 /**
9906  * @class Roo.Fx
9907  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9908  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9909  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9910  * Element effects to work.</p><br/>
9911  *
9912  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9913  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9914  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9915  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9916  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9917  * expected results and should be done with care.</p><br/>
9918  *
9919  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9920  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9921 <pre>
9922 Value  Description
9923 -----  -----------------------------
9924 tl     The top left corner
9925 t      The center of the top edge
9926 tr     The top right corner
9927 l      The center of the left edge
9928 r      The center of the right edge
9929 bl     The bottom left corner
9930 b      The center of the bottom edge
9931 br     The bottom right corner
9932 </pre>
9933  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9934  * below are common options that can be passed to any Fx method.</b>
9935  * @cfg {Function} callback A function called when the effect is finished
9936  * @cfg {Object} scope The scope of the effect function
9937  * @cfg {String} easing A valid Easing value for the effect
9938  * @cfg {String} afterCls A css class to apply after the effect
9939  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9940  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9941  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9942  * effects that end with the element being visually hidden, ignored otherwise)
9943  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9944  * a function which returns such a specification that will be applied to the Element after the effect finishes
9945  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9946  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9947  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9948  */
9949 Roo.Fx = {
9950         /**
9951          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9952          * origin for the slide effect.  This function automatically handles wrapping the element with
9953          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9954          * Usage:
9955          *<pre><code>
9956 // default: slide the element in from the top
9957 el.slideIn();
9958
9959 // custom: slide the element in from the right with a 2-second duration
9960 el.slideIn('r', { duration: 2 });
9961
9962 // common config options shown with default values
9963 el.slideIn('t', {
9964     easing: 'easeOut',
9965     duration: .5
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideIn : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // fix display to visibility
9981             this.fixDisplay();
9982
9983             // restore values after effect
9984             var r = this.getFxRestore();
9985             var b = this.getBox();
9986             // fixed size for slide
9987             this.setSize(b);
9988
9989             // wrap if needed
9990             var wrap = this.fxWrap(r.pos, o, "hidden");
9991
9992             var st = this.dom.style;
9993             st.visibility = "visible";
9994             st.position = "absolute";
9995
9996             // clear out temp styles after slide and unwrap
9997             var after = function(){
9998                 el.fxUnwrap(wrap, r.pos, o);
9999                 st.width = r.width;
10000                 st.height = r.height;
10001                 el.afterFx(o);
10002             };
10003             // time to calc the positions
10004             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10005
10006             switch(anchor.toLowerCase()){
10007                 case "t":
10008                     wrap.setSize(b.width, 0);
10009                     st.left = st.bottom = "0";
10010                     a = {height: bh};
10011                 break;
10012                 case "l":
10013                     wrap.setSize(0, b.height);
10014                     st.right = st.top = "0";
10015                     a = {width: bw};
10016                 break;
10017                 case "r":
10018                     wrap.setSize(0, b.height);
10019                     wrap.setX(b.right);
10020                     st.left = st.top = "0";
10021                     a = {width: bw, points: pt};
10022                 break;
10023                 case "b":
10024                     wrap.setSize(b.width, 0);
10025                     wrap.setY(b.bottom);
10026                     st.left = st.top = "0";
10027                     a = {height: bh, points: pt};
10028                 break;
10029                 case "tl":
10030                     wrap.setSize(0, 0);
10031                     st.right = st.bottom = "0";
10032                     a = {width: bw, height: bh};
10033                 break;
10034                 case "bl":
10035                     wrap.setSize(0, 0);
10036                     wrap.setY(b.y+b.height);
10037                     st.right = st.top = "0";
10038                     a = {width: bw, height: bh, points: pt};
10039                 break;
10040                 case "br":
10041                     wrap.setSize(0, 0);
10042                     wrap.setXY([b.right, b.bottom]);
10043                     st.left = st.top = "0";
10044                     a = {width: bw, height: bh, points: pt};
10045                 break;
10046                 case "tr":
10047                     wrap.setSize(0, 0);
10048                     wrap.setX(b.x+b.width);
10049                     st.left = st.bottom = "0";
10050                     a = {width: bw, height: bh, points: pt};
10051                 break;
10052             }
10053             this.dom.style.visibility = "visible";
10054             wrap.show();
10055
10056             arguments.callee.anim = wrap.fxanim(a,
10057                 o,
10058                 'motion',
10059                 .5,
10060                 'easeOut', after);
10061         });
10062         return this;
10063     },
10064     
10065         /**
10066          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10067          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10068          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10069          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10070          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10071          * Usage:
10072          *<pre><code>
10073 // default: slide the element out to the top
10074 el.slideOut();
10075
10076 // custom: slide the element out to the right with a 2-second duration
10077 el.slideOut('r', { duration: 2 });
10078
10079 // common config options shown with default values
10080 el.slideOut('t', {
10081     easing: 'easeOut',
10082     duration: .5,
10083     remove: false,
10084     useDisplay: false
10085 });
10086 </code></pre>
10087          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10088          * @param {Object} options (optional) Object literal with any of the Fx config options
10089          * @return {Roo.Element} The Element
10090          */
10091     slideOut : function(anchor, o){
10092         var el = this.getFxEl();
10093         o = o || {};
10094
10095         el.queueFx(o, function(){
10096
10097             anchor = anchor || "t";
10098
10099             // restore values after effect
10100             var r = this.getFxRestore();
10101             
10102             var b = this.getBox();
10103             // fixed size for slide
10104             this.setSize(b);
10105
10106             // wrap if needed
10107             var wrap = this.fxWrap(r.pos, o, "visible");
10108
10109             var st = this.dom.style;
10110             st.visibility = "visible";
10111             st.position = "absolute";
10112
10113             wrap.setSize(b);
10114
10115             var after = function(){
10116                 if(o.useDisplay){
10117                     el.setDisplayed(false);
10118                 }else{
10119                     el.hide();
10120                 }
10121
10122                 el.fxUnwrap(wrap, r.pos, o);
10123
10124                 st.width = r.width;
10125                 st.height = r.height;
10126
10127                 el.afterFx(o);
10128             };
10129
10130             var a, zero = {to: 0};
10131             switch(anchor.toLowerCase()){
10132                 case "t":
10133                     st.left = st.bottom = "0";
10134                     a = {height: zero};
10135                 break;
10136                 case "l":
10137                     st.right = st.top = "0";
10138                     a = {width: zero};
10139                 break;
10140                 case "r":
10141                     st.left = st.top = "0";
10142                     a = {width: zero, points: {to:[b.right, b.y]}};
10143                 break;
10144                 case "b":
10145                     st.left = st.top = "0";
10146                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "tl":
10149                     st.right = st.bottom = "0";
10150                     a = {width: zero, height: zero};
10151                 break;
10152                 case "bl":
10153                     st.right = st.top = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10155                 break;
10156                 case "br":
10157                     st.left = st.top = "0";
10158                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10159                 break;
10160                 case "tr":
10161                     st.left = st.bottom = "0";
10162                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10163                 break;
10164             }
10165
10166             arguments.callee.anim = wrap.fxanim(a,
10167                 o,
10168                 'motion',
10169                 .5,
10170                 "easeOut", after);
10171         });
10172         return this;
10173     },
10174
10175         /**
10176          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10177          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10178          * The element must be removed from the DOM using the 'remove' config option if desired.
10179          * Usage:
10180          *<pre><code>
10181 // default
10182 el.puff();
10183
10184 // common config options shown with default values
10185 el.puff({
10186     easing: 'easeOut',
10187     duration: .5,
10188     remove: false,
10189     useDisplay: false
10190 });
10191 </code></pre>
10192          * @param {Object} options (optional) Object literal with any of the Fx config options
10193          * @return {Roo.Element} The Element
10194          */
10195     puff : function(o){
10196         var el = this.getFxEl();
10197         o = o || {};
10198
10199         el.queueFx(o, function(){
10200             this.clearOpacity();
10201             this.show();
10202
10203             // restore values after effect
10204             var r = this.getFxRestore();
10205             var st = this.dom.style;
10206
10207             var after = function(){
10208                 if(o.useDisplay){
10209                     el.setDisplayed(false);
10210                 }else{
10211                     el.hide();
10212                 }
10213
10214                 el.clearOpacity();
10215
10216                 el.setPositioning(r.pos);
10217                 st.width = r.width;
10218                 st.height = r.height;
10219                 st.fontSize = '';
10220                 el.afterFx(o);
10221             };
10222
10223             var width = this.getWidth();
10224             var height = this.getHeight();
10225
10226             arguments.callee.anim = this.fxanim({
10227                     width : {to: this.adjustWidth(width * 2)},
10228                     height : {to: this.adjustHeight(height * 2)},
10229                     points : {by: [-(width * .5), -(height * .5)]},
10230                     opacity : {to: 0},
10231                     fontSize: {to:200, unit: "%"}
10232                 },
10233                 o,
10234                 'motion',
10235                 .5,
10236                 "easeOut", after);
10237         });
10238         return this;
10239     },
10240
10241         /**
10242          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10243          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10244          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10245          * Usage:
10246          *<pre><code>
10247 // default
10248 el.switchOff();
10249
10250 // all config options shown with default values
10251 el.switchOff({
10252     easing: 'easeIn',
10253     duration: .3,
10254     remove: false,
10255     useDisplay: false
10256 });
10257 </code></pre>
10258          * @param {Object} options (optional) Object literal with any of the Fx config options
10259          * @return {Roo.Element} The Element
10260          */
10261     switchOff : function(o){
10262         var el = this.getFxEl();
10263         o = o || {};
10264
10265         el.queueFx(o, function(){
10266             this.clearOpacity();
10267             this.clip();
10268
10269             // restore values after effect
10270             var r = this.getFxRestore();
10271             var st = this.dom.style;
10272
10273             var after = function(){
10274                 if(o.useDisplay){
10275                     el.setDisplayed(false);
10276                 }else{
10277                     el.hide();
10278                 }
10279
10280                 el.clearOpacity();
10281                 el.setPositioning(r.pos);
10282                 st.width = r.width;
10283                 st.height = r.height;
10284
10285                 el.afterFx(o);
10286             };
10287
10288             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10289                 this.clearOpacity();
10290                 (function(){
10291                     this.fxanim({
10292                         height:{to:1},
10293                         points:{by:[0, this.getHeight() * .5]}
10294                     }, o, 'motion', 0.3, 'easeIn', after);
10295                 }).defer(100, this);
10296             });
10297         });
10298         return this;
10299     },
10300
10301     /**
10302      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10303      * changed using the "attr" config option) and then fading back to the original color. If no original
10304      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10305      * Usage:
10306 <pre><code>
10307 // default: highlight background to yellow
10308 el.highlight();
10309
10310 // custom: highlight foreground text to blue for 2 seconds
10311 el.highlight("0000ff", { attr: 'color', duration: 2 });
10312
10313 // common config options shown with default values
10314 el.highlight("ffff9c", {
10315     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10316     endColor: (current color) or "ffffff",
10317     easing: 'easeIn',
10318     duration: 1
10319 });
10320 </code></pre>
10321      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10322      * @param {Object} options (optional) Object literal with any of the Fx config options
10323      * @return {Roo.Element} The Element
10324      */ 
10325     highlight : function(color, o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328
10329         el.queueFx(o, function(){
10330             color = color || "ffff9c";
10331             attr = o.attr || "backgroundColor";
10332
10333             this.clearOpacity();
10334             this.show();
10335
10336             var origColor = this.getColor(attr);
10337             var restoreColor = this.dom.style[attr];
10338             endColor = (o.endColor || origColor) || "ffffff";
10339
10340             var after = function(){
10341                 el.dom.style[attr] = restoreColor;
10342                 el.afterFx(o);
10343             };
10344
10345             var a = {};
10346             a[attr] = {from: color, to: endColor};
10347             arguments.callee.anim = this.fxanim(a,
10348                 o,
10349                 'color',
10350                 1,
10351                 'easeIn', after);
10352         });
10353         return this;
10354     },
10355
10356    /**
10357     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10358     * Usage:
10359 <pre><code>
10360 // default: a single light blue ripple
10361 el.frame();
10362
10363 // custom: 3 red ripples lasting 3 seconds total
10364 el.frame("ff0000", 3, { duration: 3 });
10365
10366 // common config options shown with default values
10367 el.frame("C3DAF9", 1, {
10368     duration: 1 //duration of entire animation (not each individual ripple)
10369     // Note: Easing is not configurable and will be ignored if included
10370 });
10371 </code></pre>
10372     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10373     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10374     * @param {Object} options (optional) Object literal with any of the Fx config options
10375     * @return {Roo.Element} The Element
10376     */
10377     frame : function(color, count, o){
10378         var el = this.getFxEl();
10379         o = o || {};
10380
10381         el.queueFx(o, function(){
10382             color = color || "#C3DAF9";
10383             if(color.length == 6){
10384                 color = "#" + color;
10385             }
10386             count = count || 1;
10387             duration = o.duration || 1;
10388             this.show();
10389
10390             var b = this.getBox();
10391             var animFn = function(){
10392                 var proxy = this.createProxy({
10393
10394                      style:{
10395                         visbility:"hidden",
10396                         position:"absolute",
10397                         "z-index":"35000", // yee haw
10398                         border:"0px solid " + color
10399                      }
10400                   });
10401                 var scale = Roo.isBorderBox ? 2 : 1;
10402                 proxy.animate({
10403                     top:{from:b.y, to:b.y - 20},
10404                     left:{from:b.x, to:b.x - 20},
10405                     borderWidth:{from:0, to:10},
10406                     opacity:{from:1, to:0},
10407                     height:{from:b.height, to:(b.height + (20*scale))},
10408                     width:{from:b.width, to:(b.width + (20*scale))}
10409                 }, duration, function(){
10410                     proxy.remove();
10411                 });
10412                 if(--count > 0){
10413                      animFn.defer((duration/2)*1000, this);
10414                 }else{
10415                     el.afterFx(o);
10416                 }
10417             };
10418             animFn.call(this);
10419         });
10420         return this;
10421     },
10422
10423    /**
10424     * Creates a pause before any subsequent queued effects begin.  If there are
10425     * no effects queued after the pause it will have no effect.
10426     * Usage:
10427 <pre><code>
10428 el.pause(1);
10429 </code></pre>
10430     * @param {Number} seconds The length of time to pause (in seconds)
10431     * @return {Roo.Element} The Element
10432     */
10433     pause : function(seconds){
10434         var el = this.getFxEl();
10435         var o = {};
10436
10437         el.queueFx(o, function(){
10438             setTimeout(function(){
10439                 el.afterFx(o);
10440             }, seconds * 1000);
10441         });
10442         return this;
10443     },
10444
10445    /**
10446     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10447     * using the "endOpacity" config option.
10448     * Usage:
10449 <pre><code>
10450 // default: fade in from opacity 0 to 100%
10451 el.fadeIn();
10452
10453 // custom: fade in from opacity 0 to 75% over 2 seconds
10454 el.fadeIn({ endOpacity: .75, duration: 2});
10455
10456 // common config options shown with default values
10457 el.fadeIn({
10458     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10459     easing: 'easeOut',
10460     duration: .5
10461 });
10462 </code></pre>
10463     * @param {Object} options (optional) Object literal with any of the Fx config options
10464     * @return {Roo.Element} The Element
10465     */
10466     fadeIn : function(o){
10467         var el = this.getFxEl();
10468         o = o || {};
10469         el.queueFx(o, function(){
10470             this.setOpacity(0);
10471             this.fixDisplay();
10472             this.dom.style.visibility = 'visible';
10473             var to = o.endOpacity || 1;
10474             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10475                 o, null, .5, "easeOut", function(){
10476                 if(to == 1){
10477                     this.clearOpacity();
10478                 }
10479                 el.afterFx(o);
10480             });
10481         });
10482         return this;
10483     },
10484
10485    /**
10486     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10487     * using the "endOpacity" config option.
10488     * Usage:
10489 <pre><code>
10490 // default: fade out from the element's current opacity to 0
10491 el.fadeOut();
10492
10493 // custom: fade out from the element's current opacity to 25% over 2 seconds
10494 el.fadeOut({ endOpacity: .25, duration: 2});
10495
10496 // common config options shown with default values
10497 el.fadeOut({
10498     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10499     easing: 'easeOut',
10500     duration: .5
10501     remove: false,
10502     useDisplay: false
10503 });
10504 </code></pre>
10505     * @param {Object} options (optional) Object literal with any of the Fx config options
10506     * @return {Roo.Element} The Element
10507     */
10508     fadeOut : function(o){
10509         var el = this.getFxEl();
10510         o = o || {};
10511         el.queueFx(o, function(){
10512             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10513                 o, null, .5, "easeOut", function(){
10514                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10515                      this.dom.style.display = "none";
10516                 }else{
10517                      this.dom.style.visibility = "hidden";
10518                 }
10519                 this.clearOpacity();
10520                 el.afterFx(o);
10521             });
10522         });
10523         return this;
10524     },
10525
10526    /**
10527     * Animates the transition of an element's dimensions from a starting height/width
10528     * to an ending height/width.
10529     * Usage:
10530 <pre><code>
10531 // change height and width to 100x100 pixels
10532 el.scale(100, 100);
10533
10534 // common config options shown with default values.  The height and width will default to
10535 // the element's existing values if passed as null.
10536 el.scale(
10537     [element's width],
10538     [element's height], {
10539     easing: 'easeOut',
10540     duration: .35
10541 });
10542 </code></pre>
10543     * @param {Number} width  The new width (pass undefined to keep the original width)
10544     * @param {Number} height  The new height (pass undefined to keep the original height)
10545     * @param {Object} options (optional) Object literal with any of the Fx config options
10546     * @return {Roo.Element} The Element
10547     */
10548     scale : function(w, h, o){
10549         this.shift(Roo.apply({}, o, {
10550             width: w,
10551             height: h
10552         }));
10553         return this;
10554     },
10555
10556    /**
10557     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10558     * Any of these properties not specified in the config object will not be changed.  This effect 
10559     * requires that at least one new dimension, position or opacity setting must be passed in on
10560     * the config object in order for the function to have any effect.
10561     * Usage:
10562 <pre><code>
10563 // slide the element horizontally to x position 200 while changing the height and opacity
10564 el.shift({ x: 200, height: 50, opacity: .8 });
10565
10566 // common config options shown with default values.
10567 el.shift({
10568     width: [element's width],
10569     height: [element's height],
10570     x: [element's x position],
10571     y: [element's y position],
10572     opacity: [element's opacity],
10573     easing: 'easeOut',
10574     duration: .35
10575 });
10576 </code></pre>
10577     * @param {Object} options  Object literal with any of the Fx config options
10578     * @return {Roo.Element} The Element
10579     */
10580     shift : function(o){
10581         var el = this.getFxEl();
10582         o = o || {};
10583         el.queueFx(o, function(){
10584             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10585             if(w !== undefined){
10586                 a.width = {to: this.adjustWidth(w)};
10587             }
10588             if(h !== undefined){
10589                 a.height = {to: this.adjustHeight(h)};
10590             }
10591             if(x !== undefined || y !== undefined){
10592                 a.points = {to: [
10593                     x !== undefined ? x : this.getX(),
10594                     y !== undefined ? y : this.getY()
10595                 ]};
10596             }
10597             if(op !== undefined){
10598                 a.opacity = {to: op};
10599             }
10600             if(o.xy !== undefined){
10601                 a.points = {to: o.xy};
10602             }
10603             arguments.callee.anim = this.fxanim(a,
10604                 o, 'motion', .35, "easeOut", function(){
10605                 el.afterFx(o);
10606             });
10607         });
10608         return this;
10609     },
10610
10611         /**
10612          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10613          * ending point of the effect.
10614          * Usage:
10615          *<pre><code>
10616 // default: slide the element downward while fading out
10617 el.ghost();
10618
10619 // custom: slide the element out to the right with a 2-second duration
10620 el.ghost('r', { duration: 2 });
10621
10622 // common config options shown with default values
10623 el.ghost('b', {
10624     easing: 'easeOut',
10625     duration: .5
10626     remove: false,
10627     useDisplay: false
10628 });
10629 </code></pre>
10630          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10631          * @param {Object} options (optional) Object literal with any of the Fx config options
10632          * @return {Roo.Element} The Element
10633          */
10634     ghost : function(anchor, o){
10635         var el = this.getFxEl();
10636         o = o || {};
10637
10638         el.queueFx(o, function(){
10639             anchor = anchor || "b";
10640
10641             // restore values after effect
10642             var r = this.getFxRestore();
10643             var w = this.getWidth(),
10644                 h = this.getHeight();
10645
10646             var st = this.dom.style;
10647
10648             var after = function(){
10649                 if(o.useDisplay){
10650                     el.setDisplayed(false);
10651                 }else{
10652                     el.hide();
10653                 }
10654
10655                 el.clearOpacity();
10656                 el.setPositioning(r.pos);
10657                 st.width = r.width;
10658                 st.height = r.height;
10659
10660                 el.afterFx(o);
10661             };
10662
10663             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10664             switch(anchor.toLowerCase()){
10665                 case "t":
10666                     pt.by = [0, -h];
10667                 break;
10668                 case "l":
10669                     pt.by = [-w, 0];
10670                 break;
10671                 case "r":
10672                     pt.by = [w, 0];
10673                 break;
10674                 case "b":
10675                     pt.by = [0, h];
10676                 break;
10677                 case "tl":
10678                     pt.by = [-w, -h];
10679                 break;
10680                 case "bl":
10681                     pt.by = [-w, h];
10682                 break;
10683                 case "br":
10684                     pt.by = [w, h];
10685                 break;
10686                 case "tr":
10687                     pt.by = [w, -h];
10688                 break;
10689             }
10690
10691             arguments.callee.anim = this.fxanim(a,
10692                 o,
10693                 'motion',
10694                 .5,
10695                 "easeOut", after);
10696         });
10697         return this;
10698     },
10699
10700         /**
10701          * Ensures that all effects queued after syncFx is called on the element are
10702          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10703          * @return {Roo.Element} The Element
10704          */
10705     syncFx : function(){
10706         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10707             block : false,
10708             concurrent : true,
10709             stopFx : false
10710         });
10711         return this;
10712     },
10713
10714         /**
10715          * Ensures that all effects queued after sequenceFx is called on the element are
10716          * run in sequence.  This is the opposite of {@link #syncFx}.
10717          * @return {Roo.Element} The Element
10718          */
10719     sequenceFx : function(){
10720         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10721             block : false,
10722             concurrent : false,
10723             stopFx : false
10724         });
10725         return this;
10726     },
10727
10728         /* @private */
10729     nextFx : function(){
10730         var ef = this.fxQueue[0];
10731         if(ef){
10732             ef.call(this);
10733         }
10734     },
10735
10736         /**
10737          * Returns true if the element has any effects actively running or queued, else returns false.
10738          * @return {Boolean} True if element has active effects, else false
10739          */
10740     hasActiveFx : function(){
10741         return this.fxQueue && this.fxQueue[0];
10742     },
10743
10744         /**
10745          * Stops any running effects and clears the element's internal effects queue if it contains
10746          * any additional effects that haven't started yet.
10747          * @return {Roo.Element} The Element
10748          */
10749     stopFx : function(){
10750         if(this.hasActiveFx()){
10751             var cur = this.fxQueue[0];
10752             if(cur && cur.anim && cur.anim.isAnimated()){
10753                 this.fxQueue = [cur]; // clear out others
10754                 cur.anim.stop(true);
10755             }
10756         }
10757         return this;
10758     },
10759
10760         /* @private */
10761     beforeFx : function(o){
10762         if(this.hasActiveFx() && !o.concurrent){
10763            if(o.stopFx){
10764                this.stopFx();
10765                return true;
10766            }
10767            return false;
10768         }
10769         return true;
10770     },
10771
10772         /**
10773          * Returns true if the element is currently blocking so that no other effect can be queued
10774          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10775          * used to ensure that an effect initiated by a user action runs to completion prior to the
10776          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10777          * @return {Boolean} True if blocking, else false
10778          */
10779     hasFxBlock : function(){
10780         var q = this.fxQueue;
10781         return q && q[0] && q[0].block;
10782     },
10783
10784         /* @private */
10785     queueFx : function(o, fn){
10786         if(!this.fxQueue){
10787             this.fxQueue = [];
10788         }
10789         if(!this.hasFxBlock()){
10790             Roo.applyIf(o, this.fxDefaults);
10791             if(!o.concurrent){
10792                 var run = this.beforeFx(o);
10793                 fn.block = o.block;
10794                 this.fxQueue.push(fn);
10795                 if(run){
10796                     this.nextFx();
10797                 }
10798             }else{
10799                 fn.call(this);
10800             }
10801         }
10802         return this;
10803     },
10804
10805         /* @private */
10806     fxWrap : function(pos, o, vis){
10807         var wrap;
10808         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10809             var wrapXY;
10810             if(o.fixPosition){
10811                 wrapXY = this.getXY();
10812             }
10813             var div = document.createElement("div");
10814             div.style.visibility = vis;
10815             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10816             wrap.setPositioning(pos);
10817             if(wrap.getStyle("position") == "static"){
10818                 wrap.position("relative");
10819             }
10820             this.clearPositioning('auto');
10821             wrap.clip();
10822             wrap.dom.appendChild(this.dom);
10823             if(wrapXY){
10824                 wrap.setXY(wrapXY);
10825             }
10826         }
10827         return wrap;
10828     },
10829
10830         /* @private */
10831     fxUnwrap : function(wrap, pos, o){
10832         this.clearPositioning();
10833         this.setPositioning(pos);
10834         if(!o.wrap){
10835             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10836             wrap.remove();
10837         }
10838     },
10839
10840         /* @private */
10841     getFxRestore : function(){
10842         var st = this.dom.style;
10843         return {pos: this.getPositioning(), width: st.width, height : st.height};
10844     },
10845
10846         /* @private */
10847     afterFx : function(o){
10848         if(o.afterStyle){
10849             this.applyStyles(o.afterStyle);
10850         }
10851         if(o.afterCls){
10852             this.addClass(o.afterCls);
10853         }
10854         if(o.remove === true){
10855             this.remove();
10856         }
10857         Roo.callback(o.callback, o.scope, [this]);
10858         if(!o.concurrent){
10859             this.fxQueue.shift();
10860             this.nextFx();
10861         }
10862     },
10863
10864         /* @private */
10865     getFxEl : function(){ // support for composite element fx
10866         return Roo.get(this.dom);
10867     },
10868
10869         /* @private */
10870     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10871         animType = animType || 'run';
10872         opt = opt || {};
10873         var anim = Roo.lib.Anim[animType](
10874             this.dom, args,
10875             (opt.duration || defaultDur) || .35,
10876             (opt.easing || defaultEase) || 'easeOut',
10877             function(){
10878                 Roo.callback(cb, this);
10879             },
10880             this
10881         );
10882         opt.anim = anim;
10883         return anim;
10884     }
10885 };
10886
10887 // backwords compat
10888 Roo.Fx.resize = Roo.Fx.scale;
10889
10890 //When included, Roo.Fx is automatically applied to Element so that all basic
10891 //effects are available directly via the Element API
10892 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10893  * Based on:
10894  * Ext JS Library 1.1.1
10895  * Copyright(c) 2006-2007, Ext JS, LLC.
10896  *
10897  * Originally Released Under LGPL - original licence link has changed is not relivant.
10898  *
10899  * Fork - LGPL
10900  * <script type="text/javascript">
10901  */
10902
10903
10904 /**
10905  * @class Roo.CompositeElement
10906  * Standard composite class. Creates a Roo.Element for every element in the collection.
10907  * <br><br>
10908  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10909  * actions will be performed on all the elements in this collection.</b>
10910  * <br><br>
10911  * All methods return <i>this</i> and can be chained.
10912  <pre><code>
10913  var els = Roo.select("#some-el div.some-class", true);
10914  // or select directly from an existing element
10915  var el = Roo.get('some-el');
10916  el.select('div.some-class', true);
10917
10918  els.setWidth(100); // all elements become 100 width
10919  els.hide(true); // all elements fade out and hide
10920  // or
10921  els.setWidth(100).hide(true);
10922  </code></pre>
10923  */
10924 Roo.CompositeElement = function(els){
10925     this.elements = [];
10926     this.addElements(els);
10927 };
10928 Roo.CompositeElement.prototype = {
10929     isComposite: true,
10930     addElements : function(els){
10931         if(!els) return this;
10932         if(typeof els == "string"){
10933             els = Roo.Element.selectorFunction(els);
10934         }
10935         var yels = this.elements;
10936         var index = yels.length-1;
10937         for(var i = 0, len = els.length; i < len; i++) {
10938                 yels[++index] = Roo.get(els[i]);
10939         }
10940         return this;
10941     },
10942
10943     /**
10944     * Clears this composite and adds the elements returned by the passed selector.
10945     * @param {String/Array} els A string CSS selector, an array of elements or an element
10946     * @return {CompositeElement} this
10947     */
10948     fill : function(els){
10949         this.elements = [];
10950         this.add(els);
10951         return this;
10952     },
10953
10954     /**
10955     * Filters this composite to only elements that match the passed selector.
10956     * @param {String} selector A string CSS selector
10957     * @param {Boolean} inverse return inverse filter (not matches)
10958     * @return {CompositeElement} this
10959     */
10960     filter : function(selector, inverse){
10961         var els = [];
10962         inverse = inverse || false;
10963         this.each(function(el){
10964             var match = inverse ? !el.is(selector) : el.is(selector);
10965             if(match){
10966                 els[els.length] = el.dom;
10967             }
10968         });
10969         this.fill(els);
10970         return this;
10971     },
10972
10973     invoke : function(fn, args){
10974         var els = this.elements;
10975         for(var i = 0, len = els.length; i < len; i++) {
10976                 Roo.Element.prototype[fn].apply(els[i], args);
10977         }
10978         return this;
10979     },
10980     /**
10981     * Adds elements to this composite.
10982     * @param {String/Array} els A string CSS selector, an array of elements or an element
10983     * @return {CompositeElement} this
10984     */
10985     add : function(els){
10986         if(typeof els == "string"){
10987             this.addElements(Roo.Element.selectorFunction(els));
10988         }else if(els.length !== undefined){
10989             this.addElements(els);
10990         }else{
10991             this.addElements([els]);
10992         }
10993         return this;
10994     },
10995     /**
10996     * Calls the passed function passing (el, this, index) for each element in this composite.
10997     * @param {Function} fn The function to call
10998     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10999     * @return {CompositeElement} this
11000     */
11001     each : function(fn, scope){
11002         var els = this.elements;
11003         for(var i = 0, len = els.length; i < len; i++){
11004             if(fn.call(scope || els[i], els[i], this, i) === false) {
11005                 break;
11006             }
11007         }
11008         return this;
11009     },
11010
11011     /**
11012      * Returns the Element object at the specified index
11013      * @param {Number} index
11014      * @return {Roo.Element}
11015      */
11016     item : function(index){
11017         return this.elements[index] || null;
11018     },
11019
11020     /**
11021      * Returns the first Element
11022      * @return {Roo.Element}
11023      */
11024     first : function(){
11025         return this.item(0);
11026     },
11027
11028     /**
11029      * Returns the last Element
11030      * @return {Roo.Element}
11031      */
11032     last : function(){
11033         return this.item(this.elements.length-1);
11034     },
11035
11036     /**
11037      * Returns the number of elements in this composite
11038      * @return Number
11039      */
11040     getCount : function(){
11041         return this.elements.length;
11042     },
11043
11044     /**
11045      * Returns true if this composite contains the passed element
11046      * @return Boolean
11047      */
11048     contains : function(el){
11049         return this.indexOf(el) !== -1;
11050     },
11051
11052     /**
11053      * Returns true if this composite contains the passed element
11054      * @return Boolean
11055      */
11056     indexOf : function(el){
11057         return this.elements.indexOf(Roo.get(el));
11058     },
11059
11060
11061     /**
11062     * Removes the specified element(s).
11063     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11064     * or an array of any of those.
11065     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11066     * @return {CompositeElement} this
11067     */
11068     removeElement : function(el, removeDom){
11069         if(el instanceof Array){
11070             for(var i = 0, len = el.length; i < len; i++){
11071                 this.removeElement(el[i]);
11072             }
11073             return this;
11074         }
11075         var index = typeof el == 'number' ? el : this.indexOf(el);
11076         if(index !== -1){
11077             if(removeDom){
11078                 var d = this.elements[index];
11079                 if(d.dom){
11080                     d.remove();
11081                 }else{
11082                     d.parentNode.removeChild(d);
11083                 }
11084             }
11085             this.elements.splice(index, 1);
11086         }
11087         return this;
11088     },
11089
11090     /**
11091     * Replaces the specified element with the passed element.
11092     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11093     * to replace.
11094     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11095     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11096     * @return {CompositeElement} this
11097     */
11098     replaceElement : function(el, replacement, domReplace){
11099         var index = typeof el == 'number' ? el : this.indexOf(el);
11100         if(index !== -1){
11101             if(domReplace){
11102                 this.elements[index].replaceWith(replacement);
11103             }else{
11104                 this.elements.splice(index, 1, Roo.get(replacement))
11105             }
11106         }
11107         return this;
11108     },
11109
11110     /**
11111      * Removes all elements.
11112      */
11113     clear : function(){
11114         this.elements = [];
11115     }
11116 };
11117 (function(){
11118     Roo.CompositeElement.createCall = function(proto, fnName){
11119         if(!proto[fnName]){
11120             proto[fnName] = function(){
11121                 return this.invoke(fnName, arguments);
11122             };
11123         }
11124     };
11125     for(var fnName in Roo.Element.prototype){
11126         if(typeof Roo.Element.prototype[fnName] == "function"){
11127             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11128         }
11129     };
11130 })();
11131 /*
11132  * Based on:
11133  * Ext JS Library 1.1.1
11134  * Copyright(c) 2006-2007, Ext JS, LLC.
11135  *
11136  * Originally Released Under LGPL - original licence link has changed is not relivant.
11137  *
11138  * Fork - LGPL
11139  * <script type="text/javascript">
11140  */
11141
11142 /**
11143  * @class Roo.CompositeElementLite
11144  * @extends Roo.CompositeElement
11145  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11146  <pre><code>
11147  var els = Roo.select("#some-el div.some-class");
11148  // or select directly from an existing element
11149  var el = Roo.get('some-el');
11150  el.select('div.some-class');
11151
11152  els.setWidth(100); // all elements become 100 width
11153  els.hide(true); // all elements fade out and hide
11154  // or
11155  els.setWidth(100).hide(true);
11156  </code></pre><br><br>
11157  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11158  * actions will be performed on all the elements in this collection.</b>
11159  */
11160 Roo.CompositeElementLite = function(els){
11161     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11162     this.el = new Roo.Element.Flyweight();
11163 };
11164 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11165     addElements : function(els){
11166         if(els){
11167             if(els instanceof Array){
11168                 this.elements = this.elements.concat(els);
11169             }else{
11170                 var yels = this.elements;
11171                 var index = yels.length-1;
11172                 for(var i = 0, len = els.length; i < len; i++) {
11173                     yels[++index] = els[i];
11174                 }
11175             }
11176         }
11177         return this;
11178     },
11179     invoke : function(fn, args){
11180         var els = this.elements;
11181         var el = this.el;
11182         for(var i = 0, len = els.length; i < len; i++) {
11183             el.dom = els[i];
11184                 Roo.Element.prototype[fn].apply(el, args);
11185         }
11186         return this;
11187     },
11188     /**
11189      * Returns a flyweight Element of the dom element object at the specified index
11190      * @param {Number} index
11191      * @return {Roo.Element}
11192      */
11193     item : function(index){
11194         if(!this.elements[index]){
11195             return null;
11196         }
11197         this.el.dom = this.elements[index];
11198         return this.el;
11199     },
11200
11201     // fixes scope with flyweight
11202     addListener : function(eventName, handler, scope, opt){
11203         var els = this.elements;
11204         for(var i = 0, len = els.length; i < len; i++) {
11205             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11206         }
11207         return this;
11208     },
11209
11210     /**
11211     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11212     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11213     * a reference to the dom node, use el.dom.</b>
11214     * @param {Function} fn The function to call
11215     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11216     * @return {CompositeElement} this
11217     */
11218     each : function(fn, scope){
11219         var els = this.elements;
11220         var el = this.el;
11221         for(var i = 0, len = els.length; i < len; i++){
11222             el.dom = els[i];
11223                 if(fn.call(scope || el, el, this, i) === false){
11224                 break;
11225             }
11226         }
11227         return this;
11228     },
11229
11230     indexOf : function(el){
11231         return this.elements.indexOf(Roo.getDom(el));
11232     },
11233
11234     replaceElement : function(el, replacement, domReplace){
11235         var index = typeof el == 'number' ? el : this.indexOf(el);
11236         if(index !== -1){
11237             replacement = Roo.getDom(replacement);
11238             if(domReplace){
11239                 var d = this.elements[index];
11240                 d.parentNode.insertBefore(replacement, d);
11241                 d.parentNode.removeChild(d);
11242             }
11243             this.elements.splice(index, 1, replacement);
11244         }
11245         return this;
11246     }
11247 });
11248 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11249
11250 /*
11251  * Based on:
11252  * Ext JS Library 1.1.1
11253  * Copyright(c) 2006-2007, Ext JS, LLC.
11254  *
11255  * Originally Released Under LGPL - original licence link has changed is not relivant.
11256  *
11257  * Fork - LGPL
11258  * <script type="text/javascript">
11259  */
11260
11261  
11262
11263 /**
11264  * @class Roo.data.Connection
11265  * @extends Roo.util.Observable
11266  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11267  * either to a configured URL, or to a URL specified at request time.<br><br>
11268  * <p>
11269  * Requests made by this class are asynchronous, and will return immediately. No data from
11270  * the server will be available to the statement immediately following the {@link #request} call.
11271  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11272  * <p>
11273  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11274  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11275  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11276  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11277  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11278  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11279  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11280  * standard DOM methods.
11281  * @constructor
11282  * @param {Object} config a configuration object.
11283  */
11284 Roo.data.Connection = function(config){
11285     Roo.apply(this, config);
11286     this.addEvents({
11287         /**
11288          * @event beforerequest
11289          * Fires before a network request is made to retrieve a data object.
11290          * @param {Connection} conn This Connection object.
11291          * @param {Object} options The options config object passed to the {@link #request} method.
11292          */
11293         "beforerequest" : true,
11294         /**
11295          * @event requestcomplete
11296          * Fires if the request was successfully completed.
11297          * @param {Connection} conn This Connection object.
11298          * @param {Object} response The XHR object containing the response data.
11299          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11300          * @param {Object} options The options config object passed to the {@link #request} method.
11301          */
11302         "requestcomplete" : true,
11303         /**
11304          * @event requestexception
11305          * Fires if an error HTTP status was returned from the server.
11306          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11307          * @param {Connection} conn This Connection object.
11308          * @param {Object} response The XHR object containing the response data.
11309          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11310          * @param {Object} options The options config object passed to the {@link #request} method.
11311          */
11312         "requestexception" : true
11313     });
11314     Roo.data.Connection.superclass.constructor.call(this);
11315 };
11316
11317 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11318     /**
11319      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11320      */
11321     /**
11322      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11323      * extra parameters to each request made by this object. (defaults to undefined)
11324      */
11325     /**
11326      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11327      *  to each request made by this object. (defaults to undefined)
11328      */
11329     /**
11330      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11331      */
11332     /**
11333      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11334      */
11335     timeout : 30000,
11336     /**
11337      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11338      * @type Boolean
11339      */
11340     autoAbort:false,
11341
11342     /**
11343      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11344      * @type Boolean
11345      */
11346     disableCaching: true,
11347
11348     /**
11349      * Sends an HTTP request to a remote server.
11350      * @param {Object} options An object which may contain the following properties:<ul>
11351      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11352      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11353      * request, a url encoded string or a function to call to get either.</li>
11354      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11355      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11356      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11357      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11358      * <li>options {Object} The parameter to the request call.</li>
11359      * <li>success {Boolean} True if the request succeeded.</li>
11360      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11361      * </ul></li>
11362      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11363      * The callback is passed the following parameters:<ul>
11364      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11365      * <li>options {Object} The parameter to the request call.</li>
11366      * </ul></li>
11367      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11368      * The callback is passed the following parameters:<ul>
11369      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11370      * <li>options {Object} The parameter to the request call.</li>
11371      * </ul></li>
11372      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11373      * for the callback function. Defaults to the browser window.</li>
11374      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11375      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11376      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11377      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11378      * params for the post data. Any params will be appended to the URL.</li>
11379      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11380      * </ul>
11381      * @return {Number} transactionId
11382      */
11383     request : function(o){
11384         if(this.fireEvent("beforerequest", this, o) !== false){
11385             var p = o.params;
11386
11387             if(typeof p == "function"){
11388                 p = p.call(o.scope||window, o);
11389             }
11390             if(typeof p == "object"){
11391                 p = Roo.urlEncode(o.params);
11392             }
11393             if(this.extraParams){
11394                 var extras = Roo.urlEncode(this.extraParams);
11395                 p = p ? (p + '&' + extras) : extras;
11396             }
11397
11398             var url = o.url || this.url;
11399             if(typeof url == 'function'){
11400                 url = url.call(o.scope||window, o);
11401             }
11402
11403             if(o.form){
11404                 var form = Roo.getDom(o.form);
11405                 url = url || form.action;
11406
11407                 var enctype = form.getAttribute("enctype");
11408                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11409                     return this.doFormUpload(o, p, url);
11410                 }
11411                 var f = Roo.lib.Ajax.serializeForm(form);
11412                 p = p ? (p + '&' + f) : f;
11413             }
11414
11415             var hs = o.headers;
11416             if(this.defaultHeaders){
11417                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11418                 if(!o.headers){
11419                     o.headers = hs;
11420                 }
11421             }
11422
11423             var cb = {
11424                 success: this.handleResponse,
11425                 failure: this.handleFailure,
11426                 scope: this,
11427                 argument: {options: o},
11428                 timeout : o.timeout || this.timeout
11429             };
11430
11431             var method = o.method||this.method||(p ? "POST" : "GET");
11432
11433             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11434                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11435             }
11436
11437             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11438                 if(o.autoAbort){
11439                     this.abort();
11440                 }
11441             }else if(this.autoAbort !== false){
11442                 this.abort();
11443             }
11444
11445             if((method == 'GET' && p) || o.xmlData){
11446                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11447                 p = '';
11448             }
11449             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11450             return this.transId;
11451         }else{
11452             Roo.callback(o.callback, o.scope, [o, null, null]);
11453             return null;
11454         }
11455     },
11456
11457     /**
11458      * Determine whether this object has a request outstanding.
11459      * @param {Number} transactionId (Optional) defaults to the last transaction
11460      * @return {Boolean} True if there is an outstanding request.
11461      */
11462     isLoading : function(transId){
11463         if(transId){
11464             return Roo.lib.Ajax.isCallInProgress(transId);
11465         }else{
11466             return this.transId ? true : false;
11467         }
11468     },
11469
11470     /**
11471      * Aborts any outstanding request.
11472      * @param {Number} transactionId (Optional) defaults to the last transaction
11473      */
11474     abort : function(transId){
11475         if(transId || this.isLoading()){
11476             Roo.lib.Ajax.abort(transId || this.transId);
11477         }
11478     },
11479
11480     // private
11481     handleResponse : function(response){
11482         this.transId = false;
11483         var options = response.argument.options;
11484         response.argument = options ? options.argument : null;
11485         this.fireEvent("requestcomplete", this, response, options);
11486         Roo.callback(options.success, options.scope, [response, options]);
11487         Roo.callback(options.callback, options.scope, [options, true, response]);
11488     },
11489
11490     // private
11491     handleFailure : function(response, e){
11492         this.transId = false;
11493         var options = response.argument.options;
11494         response.argument = options ? options.argument : null;
11495         this.fireEvent("requestexception", this, response, options, e);
11496         Roo.callback(options.failure, options.scope, [response, options]);
11497         Roo.callback(options.callback, options.scope, [options, false, response]);
11498     },
11499
11500     // private
11501     doFormUpload : function(o, ps, url){
11502         var id = Roo.id();
11503         var frame = document.createElement('iframe');
11504         frame.id = id;
11505         frame.name = id;
11506         frame.className = 'x-hidden';
11507         if(Roo.isIE){
11508             frame.src = Roo.SSL_SECURE_URL;
11509         }
11510         document.body.appendChild(frame);
11511
11512         if(Roo.isIE){
11513            document.frames[id].name = id;
11514         }
11515
11516         var form = Roo.getDom(o.form);
11517         form.target = id;
11518         form.method = 'POST';
11519         form.enctype = form.encoding = 'multipart/form-data';
11520         if(url){
11521             form.action = url;
11522         }
11523
11524         var hiddens, hd;
11525         if(ps){ // add dynamic params
11526             hiddens = [];
11527             ps = Roo.urlDecode(ps, false);
11528             for(var k in ps){
11529                 if(ps.hasOwnProperty(k)){
11530                     hd = document.createElement('input');
11531                     hd.type = 'hidden';
11532                     hd.name = k;
11533                     hd.value = ps[k];
11534                     form.appendChild(hd);
11535                     hiddens.push(hd);
11536                 }
11537             }
11538         }
11539
11540         function cb(){
11541             var r = {  // bogus response object
11542                 responseText : '',
11543                 responseXML : null
11544             };
11545
11546             r.argument = o ? o.argument : null;
11547
11548             try { //
11549                 var doc;
11550                 if(Roo.isIE){
11551                     doc = frame.contentWindow.document;
11552                 }else {
11553                     doc = (frame.contentDocument || window.frames[id].document);
11554                 }
11555                 if(doc && doc.body){
11556                     r.responseText = doc.body.innerHTML;
11557                 }
11558                 if(doc && doc.XMLDocument){
11559                     r.responseXML = doc.XMLDocument;
11560                 }else {
11561                     r.responseXML = doc;
11562                 }
11563             }
11564             catch(e) {
11565                 // ignore
11566             }
11567
11568             Roo.EventManager.removeListener(frame, 'load', cb, this);
11569
11570             this.fireEvent("requestcomplete", this, r, o);
11571             Roo.callback(o.success, o.scope, [r, o]);
11572             Roo.callback(o.callback, o.scope, [o, true, r]);
11573
11574             setTimeout(function(){document.body.removeChild(frame);}, 100);
11575         }
11576
11577         Roo.EventManager.on(frame, 'load', cb, this);
11578         form.submit();
11579
11580         if(hiddens){ // remove dynamic params
11581             for(var i = 0, len = hiddens.length; i < len; i++){
11582                 form.removeChild(hiddens[i]);
11583             }
11584         }
11585     }
11586 });
11587 /*
11588  * Based on:
11589  * Ext JS Library 1.1.1
11590  * Copyright(c) 2006-2007, Ext JS, LLC.
11591  *
11592  * Originally Released Under LGPL - original licence link has changed is not relivant.
11593  *
11594  * Fork - LGPL
11595  * <script type="text/javascript">
11596  */
11597  
11598 /**
11599  * Global Ajax request class.
11600  * 
11601  * @class Roo.Ajax
11602  * @extends Roo.data.Connection
11603  * @static
11604  * 
11605  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11606  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11607  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11608  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11609  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11610  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11611  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11612  */
11613 Roo.Ajax = new Roo.data.Connection({
11614     // fix up the docs
11615     /**
11616      * @scope Roo.Ajax
11617      * @type {Boolear} 
11618      */
11619     autoAbort : false,
11620
11621     /**
11622      * Serialize the passed form into a url encoded string
11623      * @scope Roo.Ajax
11624      * @param {String/HTMLElement} form
11625      * @return {String}
11626      */
11627     serializeForm : function(form){
11628         return Roo.lib.Ajax.serializeForm(form);
11629     }
11630 });/*
11631  * Based on:
11632  * Ext JS Library 1.1.1
11633  * Copyright(c) 2006-2007, Ext JS, LLC.
11634  *
11635  * Originally Released Under LGPL - original licence link has changed is not relivant.
11636  *
11637  * Fork - LGPL
11638  * <script type="text/javascript">
11639  */
11640
11641  
11642 /**
11643  * @class Roo.UpdateManager
11644  * @extends Roo.util.Observable
11645  * Provides AJAX-style update for Element object.<br><br>
11646  * Usage:<br>
11647  * <pre><code>
11648  * // Get it from a Roo.Element object
11649  * var el = Roo.get("foo");
11650  * var mgr = el.getUpdateManager();
11651  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11652  * ...
11653  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11654  * <br>
11655  * // or directly (returns the same UpdateManager instance)
11656  * var mgr = new Roo.UpdateManager("myElementId");
11657  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11658  * mgr.on("update", myFcnNeedsToKnow);
11659  * <br>
11660    // short handed call directly from the element object
11661    Roo.get("foo").load({
11662         url: "bar.php",
11663         scripts:true,
11664         params: "for=bar",
11665         text: "Loading Foo..."
11666    });
11667  * </code></pre>
11668  * @constructor
11669  * Create new UpdateManager directly.
11670  * @param {String/HTMLElement/Roo.Element} el The element to update
11671  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11672  */
11673 Roo.UpdateManager = function(el, forceNew){
11674     el = Roo.get(el);
11675     if(!forceNew && el.updateManager){
11676         return el.updateManager;
11677     }
11678     /**
11679      * The Element object
11680      * @type Roo.Element
11681      */
11682     this.el = el;
11683     /**
11684      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11685      * @type String
11686      */
11687     this.defaultUrl = null;
11688
11689     this.addEvents({
11690         /**
11691          * @event beforeupdate
11692          * Fired before an update is made, return false from your handler and the update is cancelled.
11693          * @param {Roo.Element} el
11694          * @param {String/Object/Function} url
11695          * @param {String/Object} params
11696          */
11697         "beforeupdate": true,
11698         /**
11699          * @event update
11700          * Fired after successful update is made.
11701          * @param {Roo.Element} el
11702          * @param {Object} oResponseObject The response Object
11703          */
11704         "update": true,
11705         /**
11706          * @event failure
11707          * Fired on update failure.
11708          * @param {Roo.Element} el
11709          * @param {Object} oResponseObject The response Object
11710          */
11711         "failure": true
11712     });
11713     var d = Roo.UpdateManager.defaults;
11714     /**
11715      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11716      * @type String
11717      */
11718     this.sslBlankUrl = d.sslBlankUrl;
11719     /**
11720      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11721      * @type Boolean
11722      */
11723     this.disableCaching = d.disableCaching;
11724     /**
11725      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11726      * @type String
11727      */
11728     this.indicatorText = d.indicatorText;
11729     /**
11730      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11731      * @type String
11732      */
11733     this.showLoadIndicator = d.showLoadIndicator;
11734     /**
11735      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11736      * @type Number
11737      */
11738     this.timeout = d.timeout;
11739
11740     /**
11741      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11742      * @type Boolean
11743      */
11744     this.loadScripts = d.loadScripts;
11745
11746     /**
11747      * Transaction object of current executing transaction
11748      */
11749     this.transaction = null;
11750
11751     /**
11752      * @private
11753      */
11754     this.autoRefreshProcId = null;
11755     /**
11756      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11757      * @type Function
11758      */
11759     this.refreshDelegate = this.refresh.createDelegate(this);
11760     /**
11761      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11762      * @type Function
11763      */
11764     this.updateDelegate = this.update.createDelegate(this);
11765     /**
11766      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11767      * @type Function
11768      */
11769     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11770     /**
11771      * @private
11772      */
11773     this.successDelegate = this.processSuccess.createDelegate(this);
11774     /**
11775      * @private
11776      */
11777     this.failureDelegate = this.processFailure.createDelegate(this);
11778
11779     if(!this.renderer){
11780      /**
11781       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11782       */
11783     this.renderer = new Roo.UpdateManager.BasicRenderer();
11784     }
11785     
11786     Roo.UpdateManager.superclass.constructor.call(this);
11787 };
11788
11789 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11790     /**
11791      * Get the Element this UpdateManager is bound to
11792      * @return {Roo.Element} The element
11793      */
11794     getEl : function(){
11795         return this.el;
11796     },
11797     /**
11798      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11799      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11800 <pre><code>
11801 um.update({<br/>
11802     url: "your-url.php",<br/>
11803     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11804     callback: yourFunction,<br/>
11805     scope: yourObject, //(optional scope)  <br/>
11806     discardUrl: false, <br/>
11807     nocache: false,<br/>
11808     text: "Loading...",<br/>
11809     timeout: 30,<br/>
11810     scripts: false<br/>
11811 });
11812 </code></pre>
11813      * The only required property is url. The optional properties nocache, text and scripts
11814      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11815      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11816      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11817      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11818      */
11819     update : function(url, params, callback, discardUrl){
11820         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11821             var method = this.method,
11822                 cfg;
11823             if(typeof url == "object"){ // must be config object
11824                 cfg = url;
11825                 url = cfg.url;
11826                 params = params || cfg.params;
11827                 callback = callback || cfg.callback;
11828                 discardUrl = discardUrl || cfg.discardUrl;
11829                 if(callback && cfg.scope){
11830                     callback = callback.createDelegate(cfg.scope);
11831                 }
11832                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11833                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11834                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11835                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11836                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11837             }
11838             this.showLoading();
11839             if(!discardUrl){
11840                 this.defaultUrl = url;
11841             }
11842             if(typeof url == "function"){
11843                 url = url.call(this);
11844             }
11845
11846             method = method || (params ? "POST" : "GET");
11847             if(method == "GET"){
11848                 url = this.prepareUrl(url);
11849             }
11850
11851             var o = Roo.apply(cfg ||{}, {
11852                 url : url,
11853                 params: params,
11854                 success: this.successDelegate,
11855                 failure: this.failureDelegate,
11856                 callback: undefined,
11857                 timeout: (this.timeout*1000),
11858                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11859             });
11860             Roo.log("updated manager called with timeout of " + o.timeout);
11861             this.transaction = Roo.Ajax.request(o);
11862         }
11863     },
11864
11865     /**
11866      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11867      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11868      * @param {String/HTMLElement} form The form Id or form element
11869      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11870      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11871      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11872      */
11873     formUpdate : function(form, url, reset, callback){
11874         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11875             if(typeof url == "function"){
11876                 url = url.call(this);
11877             }
11878             form = Roo.getDom(form);
11879             this.transaction = Roo.Ajax.request({
11880                 form: form,
11881                 url:url,
11882                 success: this.successDelegate,
11883                 failure: this.failureDelegate,
11884                 timeout: (this.timeout*1000),
11885                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11886             });
11887             this.showLoading.defer(1, this);
11888         }
11889     },
11890
11891     /**
11892      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11893      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11894      */
11895     refresh : function(callback){
11896         if(this.defaultUrl == null){
11897             return;
11898         }
11899         this.update(this.defaultUrl, null, callback, true);
11900     },
11901
11902     /**
11903      * Set this element to auto refresh.
11904      * @param {Number} interval How often to update (in seconds).
11905      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11906      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11907      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11908      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11909      */
11910     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11911         if(refreshNow){
11912             this.update(url || this.defaultUrl, params, callback, true);
11913         }
11914         if(this.autoRefreshProcId){
11915             clearInterval(this.autoRefreshProcId);
11916         }
11917         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11918     },
11919
11920     /**
11921      * Stop auto refresh on this element.
11922      */
11923      stopAutoRefresh : function(){
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926             delete this.autoRefreshProcId;
11927         }
11928     },
11929
11930     isAutoRefreshing : function(){
11931        return this.autoRefreshProcId ? true : false;
11932     },
11933     /**
11934      * Called to update the element to "Loading" state. Override to perform custom action.
11935      */
11936     showLoading : function(){
11937         if(this.showLoadIndicator){
11938             this.el.update(this.indicatorText);
11939         }
11940     },
11941
11942     /**
11943      * Adds unique parameter to query string if disableCaching = true
11944      * @private
11945      */
11946     prepareUrl : function(url){
11947         if(this.disableCaching){
11948             var append = "_dc=" + (new Date().getTime());
11949             if(url.indexOf("?") !== -1){
11950                 url += "&" + append;
11951             }else{
11952                 url += "?" + append;
11953             }
11954         }
11955         return url;
11956     },
11957
11958     /**
11959      * @private
11960      */
11961     processSuccess : function(response){
11962         this.transaction = null;
11963         if(response.argument.form && response.argument.reset){
11964             try{ // put in try/catch since some older FF releases had problems with this
11965                 response.argument.form.reset();
11966             }catch(e){}
11967         }
11968         if(this.loadScripts){
11969             this.renderer.render(this.el, response, this,
11970                 this.updateComplete.createDelegate(this, [response]));
11971         }else{
11972             this.renderer.render(this.el, response, this);
11973             this.updateComplete(response);
11974         }
11975     },
11976
11977     updateComplete : function(response){
11978         this.fireEvent("update", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, true, response);
11981         }
11982     },
11983
11984     /**
11985      * @private
11986      */
11987     processFailure : function(response){
11988         this.transaction = null;
11989         this.fireEvent("failure", this.el, response);
11990         if(typeof response.argument.callback == "function"){
11991             response.argument.callback(this.el, false, response);
11992         }
11993     },
11994
11995     /**
11996      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11997      * @param {Object} renderer The object implementing the render() method
11998      */
11999     setRenderer : function(renderer){
12000         this.renderer = renderer;
12001     },
12002
12003     getRenderer : function(){
12004        return this.renderer;
12005     },
12006
12007     /**
12008      * Set the defaultUrl used for updates
12009      * @param {String/Function} defaultUrl The url or a function to call to get the url
12010      */
12011     setDefaultUrl : function(defaultUrl){
12012         this.defaultUrl = defaultUrl;
12013     },
12014
12015     /**
12016      * Aborts the executing transaction
12017      */
12018     abort : function(){
12019         if(this.transaction){
12020             Roo.Ajax.abort(this.transaction);
12021         }
12022     },
12023
12024     /**
12025      * Returns true if an update is in progress
12026      * @return {Boolean}
12027      */
12028     isUpdating : function(){
12029         if(this.transaction){
12030             return Roo.Ajax.isLoading(this.transaction);
12031         }
12032         return false;
12033     }
12034 });
12035
12036 /**
12037  * @class Roo.UpdateManager.defaults
12038  * @static (not really - but it helps the doc tool)
12039  * The defaults collection enables customizing the default properties of UpdateManager
12040  */
12041    Roo.UpdateManager.defaults = {
12042        /**
12043          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12044          * @type Number
12045          */
12046          timeout : 30,
12047
12048          /**
12049          * True to process scripts by default (Defaults to false).
12050          * @type Boolean
12051          */
12052         loadScripts : false,
12053
12054         /**
12055         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12056         * @type String
12057         */
12058         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12059         /**
12060          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12061          * @type Boolean
12062          */
12063         disableCaching : false,
12064         /**
12065          * Whether to show indicatorText when loading (Defaults to true).
12066          * @type Boolean
12067          */
12068         showLoadIndicator : true,
12069         /**
12070          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12071          * @type String
12072          */
12073         indicatorText : '<div class="loading-indicator">Loading...</div>'
12074    };
12075
12076 /**
12077  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12078  *Usage:
12079  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12080  * @param {String/HTMLElement/Roo.Element} el The element to update
12081  * @param {String} url The url
12082  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12083  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12084  * @static
12085  * @deprecated
12086  * @member Roo.UpdateManager
12087  */
12088 Roo.UpdateManager.updateElement = function(el, url, params, options){
12089     var um = Roo.get(el, true).getUpdateManager();
12090     Roo.apply(um, options);
12091     um.update(url, params, options ? options.callback : null);
12092 };
12093 // alias for backwards compat
12094 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12095 /**
12096  * @class Roo.UpdateManager.BasicRenderer
12097  * Default Content renderer. Updates the elements innerHTML with the responseText.
12098  */
12099 Roo.UpdateManager.BasicRenderer = function(){};
12100
12101 Roo.UpdateManager.BasicRenderer.prototype = {
12102     /**
12103      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12104      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12105      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12106      * @param {Roo.Element} el The element being rendered
12107      * @param {Object} response The YUI Connect response object
12108      * @param {UpdateManager} updateManager The calling update manager
12109      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12110      */
12111      render : function(el, response, updateManager, callback){
12112         el.update(response.responseText, updateManager.loadScripts, callback);
12113     }
12114 };
12115 /*
12116  * Based on:
12117  * Roo JS
12118  * (c)) Alan Knowles
12119  * Licence : LGPL
12120  */
12121
12122
12123 /**
12124  * @class Roo.DomTemplate
12125  * @extends Roo.Template
12126  * An effort at a dom based template engine..
12127  *
12128  * Similar to XTemplate, except it uses dom parsing to create the template..
12129  *
12130  * Supported features:
12131  *
12132  *  Tags:
12133
12134 <pre><code>
12135       {a_variable} - output encoded.
12136       {a_variable.format:("Y-m-d")} - call a method on the variable
12137       {a_variable:raw} - unencoded output
12138       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12139       {a_variable:this.method_on_template(...)} - call a method on the template object.
12140  
12141 </code></pre>
12142  *  The tpl tag:
12143 <pre><code>
12144         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12145         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12146         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12147         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12148   
12149 </code></pre>
12150  *      
12151  */
12152 Roo.DomTemplate = function()
12153 {
12154      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12155      if (this.html) {
12156         this.compile();
12157      }
12158 };
12159
12160
12161 Roo.extend(Roo.DomTemplate, Roo.Template, {
12162     /**
12163      * id counter for sub templates.
12164      */
12165     id : 0,
12166     /**
12167      * flag to indicate if dom parser is inside a pre,
12168      * it will strip whitespace if not.
12169      */
12170     inPre : false,
12171     
12172     /**
12173      * The various sub templates
12174      */
12175     tpls : false,
12176     
12177     
12178     
12179     /**
12180      *
12181      * basic tag replacing syntax
12182      * WORD:WORD()
12183      *
12184      * // you can fake an object call by doing this
12185      *  x.t:(test,tesT) 
12186      * 
12187      */
12188     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12189     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12190     
12191     iterChild : function (node, method) {
12192         
12193         var oldPre = this.inPre;
12194         if (node.tagName == 'PRE') {
12195             this.inPre = true;
12196         }
12197         for( var i = 0; i < node.childNodes.length; i++) {
12198             method.call(this, node.childNodes[i]);
12199         }
12200         this.inPre = oldPre;
12201     },
12202     
12203     
12204     
12205     /**
12206      * compile the template
12207      *
12208      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12209      *
12210      */
12211     compile: function()
12212     {
12213         var s = this.html;
12214         
12215         // covert the html into DOM...
12216         var doc = false;
12217         var div =false;
12218         try {
12219             doc = document.implementation.createHTMLDocument("");
12220             doc.documentElement.innerHTML =   this.html  ;
12221             div = doc.documentElement;
12222         } catch (e) {
12223             // old IE... - nasty -- it causes all sorts of issues.. with
12224             // images getting pulled from server..
12225             div = document.createElement('div');
12226             div.innerHTML = this.html;
12227         }
12228         //doc.documentElement.innerHTML = htmlBody
12229          
12230         
12231         
12232         this.tpls = [];
12233         var _t = this;
12234         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12235         
12236         var tpls = this.tpls;
12237         
12238         // create a top level template from the snippet..
12239         
12240         //Roo.log(div.innerHTML);
12241         
12242         var tpl = {
12243             uid : 'master',
12244             id : this.id++,
12245             attr : false,
12246             value : false,
12247             body : div.innerHTML,
12248             
12249             forCall : false,
12250             execCall : false,
12251             dom : div,
12252             isTop : true
12253             
12254         };
12255         tpls.unshift(tpl);
12256         
12257         
12258         // compile them...
12259         this.tpls = [];
12260         Roo.each(tpls, function(tp){
12261             this.compileTpl(tp);
12262             this.tpls[tp.id] = tp;
12263         }, this);
12264         
12265         this.master = tpls[0];
12266         return this;
12267         
12268         
12269     },
12270     
12271     compileNode : function(node, istop) {
12272         // test for
12273         //Roo.log(node);
12274         
12275         
12276         // skip anything not a tag..
12277         if (node.nodeType != 1) {
12278             if (node.nodeType == 3 && !this.inPre) {
12279                 // reduce white space..
12280                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12281                 
12282             }
12283             return;
12284         }
12285         
12286         var tpl = {
12287             uid : false,
12288             id : false,
12289             attr : false,
12290             value : false,
12291             body : '',
12292             
12293             forCall : false,
12294             execCall : false,
12295             dom : false,
12296             isTop : istop
12297             
12298             
12299         };
12300         
12301         
12302         switch(true) {
12303             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12304             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12305             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12306             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12307             // no default..
12308         }
12309         
12310         
12311         if (!tpl.attr) {
12312             // just itterate children..
12313             this.iterChild(node,this.compileNode);
12314             return;
12315         }
12316         tpl.uid = this.id++;
12317         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12318         node.removeAttribute('roo-'+ tpl.attr);
12319         if (tpl.attr != 'name') {
12320             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12321             node.parentNode.replaceChild(placeholder,  node);
12322         } else {
12323             
12324             var placeholder =  document.createElement('span');
12325             placeholder.className = 'roo-tpl-' + tpl.value;
12326             node.parentNode.replaceChild(placeholder,  node);
12327         }
12328         
12329         // parent now sees '{domtplXXXX}
12330         this.iterChild(node,this.compileNode);
12331         
12332         // we should now have node body...
12333         var div = document.createElement('div');
12334         div.appendChild(node);
12335         tpl.dom = node;
12336         // this has the unfortunate side effect of converting tagged attributes
12337         // eg. href="{...}" into %7C...%7D
12338         // this has been fixed by searching for those combo's although it's a bit hacky..
12339         
12340         
12341         tpl.body = div.innerHTML;
12342         
12343         
12344          
12345         tpl.id = tpl.uid;
12346         switch(tpl.attr) {
12347             case 'for' :
12348                 switch (tpl.value) {
12349                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12350                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12351                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12352                 }
12353                 break;
12354             
12355             case 'exec':
12356                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12357                 break;
12358             
12359             case 'if':     
12360                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12361                 break;
12362             
12363             case 'name':
12364                 tpl.id  = tpl.value; // replace non characters???
12365                 break;
12366             
12367         }
12368         
12369         
12370         this.tpls.push(tpl);
12371         
12372         
12373         
12374     },
12375     
12376     
12377     
12378     
12379     /**
12380      * Compile a segment of the template into a 'sub-template'
12381      *
12382      * 
12383      * 
12384      *
12385      */
12386     compileTpl : function(tpl)
12387     {
12388         var fm = Roo.util.Format;
12389         var useF = this.disableFormats !== true;
12390         
12391         var sep = Roo.isGecko ? "+\n" : ",\n";
12392         
12393         var undef = function(str) {
12394             Roo.debug && Roo.log("Property not found :"  + str);
12395             return '';
12396         };
12397           
12398         //Roo.log(tpl.body);
12399         
12400         
12401         
12402         var fn = function(m, lbrace, name, format, args)
12403         {
12404             //Roo.log("ARGS");
12405             //Roo.log(arguments);
12406             args = args ? args.replace(/\\'/g,"'") : args;
12407             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12408             if (typeof(format) == 'undefined') {
12409                 format =  'htmlEncode'; 
12410             }
12411             if (format == 'raw' ) {
12412                 format = false;
12413             }
12414             
12415             if(name.substr(0, 6) == 'domtpl'){
12416                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12417             }
12418             
12419             // build an array of options to determine if value is undefined..
12420             
12421             // basically get 'xxxx.yyyy' then do
12422             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12423             //    (function () { Roo.log("Property not found"); return ''; })() :
12424             //    ......
12425             
12426             var udef_ar = [];
12427             var lookfor = '';
12428             Roo.each(name.split('.'), function(st) {
12429                 lookfor += (lookfor.length ? '.': '') + st;
12430                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12431             });
12432             
12433             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12434             
12435             
12436             if(format && useF){
12437                 
12438                 args = args ? ',' + args : "";
12439                  
12440                 if(format.substr(0, 5) != "this."){
12441                     format = "fm." + format + '(';
12442                 }else{
12443                     format = 'this.call("'+ format.substr(5) + '", ';
12444                     args = ", values";
12445                 }
12446                 
12447                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12448             }
12449              
12450             if (args && args.length) {
12451                 // called with xxyx.yuu:(test,test)
12452                 // change to ()
12453                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12454             }
12455             // raw.. - :raw modifier..
12456             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12457             
12458         };
12459         var body;
12460         // branched to use + in gecko and [].join() in others
12461         if(Roo.isGecko){
12462             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12463                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12464                     "';};};";
12465         }else{
12466             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12467             body.push(tpl.body.replace(/(\r\n|\n)/g,
12468                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12469             body.push("'].join('');};};");
12470             body = body.join('');
12471         }
12472         
12473         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12474        
12475         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12476         eval(body);
12477         
12478         return this;
12479     },
12480      
12481     /**
12482      * same as applyTemplate, except it's done to one of the subTemplates
12483      * when using named templates, you can do:
12484      *
12485      * var str = pl.applySubTemplate('your-name', values);
12486      *
12487      * 
12488      * @param {Number} id of the template
12489      * @param {Object} values to apply to template
12490      * @param {Object} parent (normaly the instance of this object)
12491      */
12492     applySubTemplate : function(id, values, parent)
12493     {
12494         
12495         
12496         var t = this.tpls[id];
12497         
12498         
12499         try { 
12500             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12501                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507           
12508             return '';
12509         }
12510         try { 
12511             
12512             if(t.execCall && t.execCall.call(this, values, parent)){
12513                 return '';
12514             }
12515         } catch(e) {
12516             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12517             Roo.log(values);
12518             return '';
12519         }
12520         
12521         try {
12522             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12523             parent = t.target ? values : parent;
12524             if(t.forCall && vs instanceof Array){
12525                 var buf = [];
12526                 for(var i = 0, len = vs.length; i < len; i++){
12527                     try {
12528                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12529                     } catch (e) {
12530                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12531                         Roo.log(e.body);
12532                         //Roo.log(t.compiled);
12533                         Roo.log(vs[i]);
12534                     }   
12535                 }
12536                 return buf.join('');
12537             }
12538         } catch (e) {
12539             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12540             Roo.log(values);
12541             return '';
12542         }
12543         try {
12544             return t.compiled.call(this, vs, parent);
12545         } catch (e) {
12546             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12547             Roo.log(e.body);
12548             //Roo.log(t.compiled);
12549             Roo.log(values);
12550             return '';
12551         }
12552     },
12553
12554    
12555
12556     applyTemplate : function(values){
12557         return this.master.compiled.call(this, values, {});
12558         //var s = this.subs;
12559     },
12560
12561     apply : function(){
12562         return this.applyTemplate.apply(this, arguments);
12563     }
12564
12565  });
12566
12567 Roo.DomTemplate.from = function(el){
12568     el = Roo.getDom(el);
12569     return new Roo.Domtemplate(el.value || el.innerHTML);
12570 };/*
12571  * Based on:
12572  * Ext JS Library 1.1.1
12573  * Copyright(c) 2006-2007, Ext JS, LLC.
12574  *
12575  * Originally Released Under LGPL - original licence link has changed is not relivant.
12576  *
12577  * Fork - LGPL
12578  * <script type="text/javascript">
12579  */
12580
12581 /**
12582  * @class Roo.util.DelayedTask
12583  * Provides a convenient method of performing setTimeout where a new
12584  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12585  * You can use this class to buffer
12586  * the keypress events for a certain number of milliseconds, and perform only if they stop
12587  * for that amount of time.
12588  * @constructor The parameters to this constructor serve as defaults and are not required.
12589  * @param {Function} fn (optional) The default function to timeout
12590  * @param {Object} scope (optional) The default scope of that timeout
12591  * @param {Array} args (optional) The default Array of arguments
12592  */
12593 Roo.util.DelayedTask = function(fn, scope, args){
12594     var id = null, d, t;
12595
12596     var call = function(){
12597         var now = new Date().getTime();
12598         if(now - t >= d){
12599             clearInterval(id);
12600             id = null;
12601             fn.apply(scope, args || []);
12602         }
12603     };
12604     /**
12605      * Cancels any pending timeout and queues a new one
12606      * @param {Number} delay The milliseconds to delay
12607      * @param {Function} newFn (optional) Overrides function passed to constructor
12608      * @param {Object} newScope (optional) Overrides scope passed to constructor
12609      * @param {Array} newArgs (optional) Overrides args passed to constructor
12610      */
12611     this.delay = function(delay, newFn, newScope, newArgs){
12612         if(id && delay != d){
12613             this.cancel();
12614         }
12615         d = delay;
12616         t = new Date().getTime();
12617         fn = newFn || fn;
12618         scope = newScope || scope;
12619         args = newArgs || args;
12620         if(!id){
12621             id = setInterval(call, d);
12622         }
12623     };
12624
12625     /**
12626      * Cancel the last queued timeout
12627      */
12628     this.cancel = function(){
12629         if(id){
12630             clearInterval(id);
12631             id = null;
12632         }
12633     };
12634 };/*
12635  * Based on:
12636  * Ext JS Library 1.1.1
12637  * Copyright(c) 2006-2007, Ext JS, LLC.
12638  *
12639  * Originally Released Under LGPL - original licence link has changed is not relivant.
12640  *
12641  * Fork - LGPL
12642  * <script type="text/javascript">
12643  */
12644  
12645  
12646 Roo.util.TaskRunner = function(interval){
12647     interval = interval || 10;
12648     var tasks = [], removeQueue = [];
12649     var id = 0;
12650     var running = false;
12651
12652     var stopThread = function(){
12653         running = false;
12654         clearInterval(id);
12655         id = 0;
12656     };
12657
12658     var startThread = function(){
12659         if(!running){
12660             running = true;
12661             id = setInterval(runTasks, interval);
12662         }
12663     };
12664
12665     var removeTask = function(task){
12666         removeQueue.push(task);
12667         if(task.onStop){
12668             task.onStop();
12669         }
12670     };
12671
12672     var runTasks = function(){
12673         if(removeQueue.length > 0){
12674             for(var i = 0, len = removeQueue.length; i < len; i++){
12675                 tasks.remove(removeQueue[i]);
12676             }
12677             removeQueue = [];
12678             if(tasks.length < 1){
12679                 stopThread();
12680                 return;
12681             }
12682         }
12683         var now = new Date().getTime();
12684         for(var i = 0, len = tasks.length; i < len; ++i){
12685             var t = tasks[i];
12686             var itime = now - t.taskRunTime;
12687             if(t.interval <= itime){
12688                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12689                 t.taskRunTime = now;
12690                 if(rt === false || t.taskRunCount === t.repeat){
12691                     removeTask(t);
12692                     return;
12693                 }
12694             }
12695             if(t.duration && t.duration <= (now - t.taskStartTime)){
12696                 removeTask(t);
12697             }
12698         }
12699     };
12700
12701     /**
12702      * Queues a new task.
12703      * @param {Object} task
12704      */
12705     this.start = function(task){
12706         tasks.push(task);
12707         task.taskStartTime = new Date().getTime();
12708         task.taskRunTime = 0;
12709         task.taskRunCount = 0;
12710         startThread();
12711         return task;
12712     };
12713
12714     this.stop = function(task){
12715         removeTask(task);
12716         return task;
12717     };
12718
12719     this.stopAll = function(){
12720         stopThread();
12721         for(var i = 0, len = tasks.length; i < len; i++){
12722             if(tasks[i].onStop){
12723                 tasks[i].onStop();
12724             }
12725         }
12726         tasks = [];
12727         removeQueue = [];
12728     };
12729 };
12730
12731 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12732  * Based on:
12733  * Ext JS Library 1.1.1
12734  * Copyright(c) 2006-2007, Ext JS, LLC.
12735  *
12736  * Originally Released Under LGPL - original licence link has changed is not relivant.
12737  *
12738  * Fork - LGPL
12739  * <script type="text/javascript">
12740  */
12741
12742  
12743 /**
12744  * @class Roo.util.MixedCollection
12745  * @extends Roo.util.Observable
12746  * A Collection class that maintains both numeric indexes and keys and exposes events.
12747  * @constructor
12748  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12749  * collection (defaults to false)
12750  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12751  * and return the key value for that item.  This is used when available to look up the key on items that
12752  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12753  * equivalent to providing an implementation for the {@link #getKey} method.
12754  */
12755 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12756     this.items = [];
12757     this.map = {};
12758     this.keys = [];
12759     this.length = 0;
12760     this.addEvents({
12761         /**
12762          * @event clear
12763          * Fires when the collection is cleared.
12764          */
12765         "clear" : true,
12766         /**
12767          * @event add
12768          * Fires when an item is added to the collection.
12769          * @param {Number} index The index at which the item was added.
12770          * @param {Object} o The item added.
12771          * @param {String} key The key associated with the added item.
12772          */
12773         "add" : true,
12774         /**
12775          * @event replace
12776          * Fires when an item is replaced in the collection.
12777          * @param {String} key he key associated with the new added.
12778          * @param {Object} old The item being replaced.
12779          * @param {Object} new The new item.
12780          */
12781         "replace" : true,
12782         /**
12783          * @event remove
12784          * Fires when an item is removed from the collection.
12785          * @param {Object} o The item being removed.
12786          * @param {String} key (optional) The key associated with the removed item.
12787          */
12788         "remove" : true,
12789         "sort" : true
12790     });
12791     this.allowFunctions = allowFunctions === true;
12792     if(keyFn){
12793         this.getKey = keyFn;
12794     }
12795     Roo.util.MixedCollection.superclass.constructor.call(this);
12796 };
12797
12798 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12799     allowFunctions : false,
12800     
12801 /**
12802  * Adds an item to the collection.
12803  * @param {String} key The key to associate with the item
12804  * @param {Object} o The item to add.
12805  * @return {Object} The item added.
12806  */
12807     add : function(key, o){
12808         if(arguments.length == 1){
12809             o = arguments[0];
12810             key = this.getKey(o);
12811         }
12812         if(typeof key == "undefined" || key === null){
12813             this.length++;
12814             this.items.push(o);
12815             this.keys.push(null);
12816         }else{
12817             var old = this.map[key];
12818             if(old){
12819                 return this.replace(key, o);
12820             }
12821             this.length++;
12822             this.items.push(o);
12823             this.map[key] = o;
12824             this.keys.push(key);
12825         }
12826         this.fireEvent("add", this.length-1, o, key);
12827         return o;
12828     },
12829        
12830 /**
12831   * MixedCollection has a generic way to fetch keys if you implement getKey.
12832 <pre><code>
12833 // normal way
12834 var mc = new Roo.util.MixedCollection();
12835 mc.add(someEl.dom.id, someEl);
12836 mc.add(otherEl.dom.id, otherEl);
12837 //and so on
12838
12839 // using getKey
12840 var mc = new Roo.util.MixedCollection();
12841 mc.getKey = function(el){
12842    return el.dom.id;
12843 };
12844 mc.add(someEl);
12845 mc.add(otherEl);
12846
12847 // or via the constructor
12848 var mc = new Roo.util.MixedCollection(false, function(el){
12849    return el.dom.id;
12850 });
12851 mc.add(someEl);
12852 mc.add(otherEl);
12853 </code></pre>
12854  * @param o {Object} The item for which to find the key.
12855  * @return {Object} The key for the passed item.
12856  */
12857     getKey : function(o){
12858          return o.id; 
12859     },
12860    
12861 /**
12862  * Replaces an item in the collection.
12863  * @param {String} key The key associated with the item to replace, or the item to replace.
12864  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12865  * @return {Object}  The new item.
12866  */
12867     replace : function(key, o){
12868         if(arguments.length == 1){
12869             o = arguments[0];
12870             key = this.getKey(o);
12871         }
12872         var old = this.item(key);
12873         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12874              return this.add(key, o);
12875         }
12876         var index = this.indexOfKey(key);
12877         this.items[index] = o;
12878         this.map[key] = o;
12879         this.fireEvent("replace", key, old, o);
12880         return o;
12881     },
12882    
12883 /**
12884  * Adds all elements of an Array or an Object to the collection.
12885  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12886  * an Array of values, each of which are added to the collection.
12887  */
12888     addAll : function(objs){
12889         if(arguments.length > 1 || objs instanceof Array){
12890             var args = arguments.length > 1 ? arguments : objs;
12891             for(var i = 0, len = args.length; i < len; i++){
12892                 this.add(args[i]);
12893             }
12894         }else{
12895             for(var key in objs){
12896                 if(this.allowFunctions || typeof objs[key] != "function"){
12897                     this.add(key, objs[key]);
12898                 }
12899             }
12900         }
12901     },
12902    
12903 /**
12904  * Executes the specified function once for every item in the collection, passing each
12905  * item as the first and only parameter. returning false from the function will stop the iteration.
12906  * @param {Function} fn The function to execute for each item.
12907  * @param {Object} scope (optional) The scope in which to execute the function.
12908  */
12909     each : function(fn, scope){
12910         var items = [].concat(this.items); // each safe for removal
12911         for(var i = 0, len = items.length; i < len; i++){
12912             if(fn.call(scope || items[i], items[i], i, len) === false){
12913                 break;
12914             }
12915         }
12916     },
12917    
12918 /**
12919  * Executes the specified function once for every key in the collection, passing each
12920  * key, and its associated item as the first two parameters.
12921  * @param {Function} fn The function to execute for each item.
12922  * @param {Object} scope (optional) The scope in which to execute the function.
12923  */
12924     eachKey : function(fn, scope){
12925         for(var i = 0, len = this.keys.length; i < len; i++){
12926             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12927         }
12928     },
12929    
12930 /**
12931  * Returns the first item in the collection which elicits a true return value from the
12932  * passed selection function.
12933  * @param {Function} fn The selection function to execute for each item.
12934  * @param {Object} scope (optional) The scope in which to execute the function.
12935  * @return {Object} The first item in the collection which returned true from the selection function.
12936  */
12937     find : function(fn, scope){
12938         for(var i = 0, len = this.items.length; i < len; i++){
12939             if(fn.call(scope || window, this.items[i], this.keys[i])){
12940                 return this.items[i];
12941             }
12942         }
12943         return null;
12944     },
12945    
12946 /**
12947  * Inserts an item at the specified index in the collection.
12948  * @param {Number} index The index to insert the item at.
12949  * @param {String} key The key to associate with the new item, or the item itself.
12950  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12951  * @return {Object} The item inserted.
12952  */
12953     insert : function(index, key, o){
12954         if(arguments.length == 2){
12955             o = arguments[1];
12956             key = this.getKey(o);
12957         }
12958         if(index >= this.length){
12959             return this.add(key, o);
12960         }
12961         this.length++;
12962         this.items.splice(index, 0, o);
12963         if(typeof key != "undefined" && key != null){
12964             this.map[key] = o;
12965         }
12966         this.keys.splice(index, 0, key);
12967         this.fireEvent("add", index, o, key);
12968         return o;
12969     },
12970    
12971 /**
12972  * Removed an item from the collection.
12973  * @param {Object} o The item to remove.
12974  * @return {Object} The item removed.
12975  */
12976     remove : function(o){
12977         return this.removeAt(this.indexOf(o));
12978     },
12979    
12980 /**
12981  * Remove an item from a specified index in the collection.
12982  * @param {Number} index The index within the collection of the item to remove.
12983  */
12984     removeAt : function(index){
12985         if(index < this.length && index >= 0){
12986             this.length--;
12987             var o = this.items[index];
12988             this.items.splice(index, 1);
12989             var key = this.keys[index];
12990             if(typeof key != "undefined"){
12991                 delete this.map[key];
12992             }
12993             this.keys.splice(index, 1);
12994             this.fireEvent("remove", o, key);
12995         }
12996     },
12997    
12998 /**
12999  * Removed an item associated with the passed key fom the collection.
13000  * @param {String} key The key of the item to remove.
13001  */
13002     removeKey : function(key){
13003         return this.removeAt(this.indexOfKey(key));
13004     },
13005    
13006 /**
13007  * Returns the number of items in the collection.
13008  * @return {Number} the number of items in the collection.
13009  */
13010     getCount : function(){
13011         return this.length; 
13012     },
13013    
13014 /**
13015  * Returns index within the collection of the passed Object.
13016  * @param {Object} o The item to find the index of.
13017  * @return {Number} index of the item.
13018  */
13019     indexOf : function(o){
13020         if(!this.items.indexOf){
13021             for(var i = 0, len = this.items.length; i < len; i++){
13022                 if(this.items[i] == o) return i;
13023             }
13024             return -1;
13025         }else{
13026             return this.items.indexOf(o);
13027         }
13028     },
13029    
13030 /**
13031  * Returns index within the collection of the passed key.
13032  * @param {String} key The key to find the index of.
13033  * @return {Number} index of the key.
13034  */
13035     indexOfKey : function(key){
13036         if(!this.keys.indexOf){
13037             for(var i = 0, len = this.keys.length; i < len; i++){
13038                 if(this.keys[i] == key) return i;
13039             }
13040             return -1;
13041         }else{
13042             return this.keys.indexOf(key);
13043         }
13044     },
13045    
13046 /**
13047  * Returns the item associated with the passed key OR index. Key has priority over index.
13048  * @param {String/Number} key The key or index of the item.
13049  * @return {Object} The item associated with the passed key.
13050  */
13051     item : function(key){
13052         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13053         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13054     },
13055     
13056 /**
13057  * Returns the item at the specified index.
13058  * @param {Number} index The index of the item.
13059  * @return {Object}
13060  */
13061     itemAt : function(index){
13062         return this.items[index];
13063     },
13064     
13065 /**
13066  * Returns the item associated with the passed key.
13067  * @param {String/Number} key The key of the item.
13068  * @return {Object} The item associated with the passed key.
13069  */
13070     key : function(key){
13071         return this.map[key];
13072     },
13073    
13074 /**
13075  * Returns true if the collection contains the passed Object as an item.
13076  * @param {Object} o  The Object to look for in the collection.
13077  * @return {Boolean} True if the collection contains the Object as an item.
13078  */
13079     contains : function(o){
13080         return this.indexOf(o) != -1;
13081     },
13082    
13083 /**
13084  * Returns true if the collection contains the passed Object as a key.
13085  * @param {String} key The key to look for in the collection.
13086  * @return {Boolean} True if the collection contains the Object as a key.
13087  */
13088     containsKey : function(key){
13089         return typeof this.map[key] != "undefined";
13090     },
13091    
13092 /**
13093  * Removes all items from the collection.
13094  */
13095     clear : function(){
13096         this.length = 0;
13097         this.items = [];
13098         this.keys = [];
13099         this.map = {};
13100         this.fireEvent("clear");
13101     },
13102    
13103 /**
13104  * Returns the first item in the collection.
13105  * @return {Object} the first item in the collection..
13106  */
13107     first : function(){
13108         return this.items[0]; 
13109     },
13110    
13111 /**
13112  * Returns the last item in the collection.
13113  * @return {Object} the last item in the collection..
13114  */
13115     last : function(){
13116         return this.items[this.length-1];   
13117     },
13118     
13119     _sort : function(property, dir, fn){
13120         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13121         fn = fn || function(a, b){
13122             return a-b;
13123         };
13124         var c = [], k = this.keys, items = this.items;
13125         for(var i = 0, len = items.length; i < len; i++){
13126             c[c.length] = {key: k[i], value: items[i], index: i};
13127         }
13128         c.sort(function(a, b){
13129             var v = fn(a[property], b[property]) * dsc;
13130             if(v == 0){
13131                 v = (a.index < b.index ? -1 : 1);
13132             }
13133             return v;
13134         });
13135         for(var i = 0, len = c.length; i < len; i++){
13136             items[i] = c[i].value;
13137             k[i] = c[i].key;
13138         }
13139         this.fireEvent("sort", this);
13140     },
13141     
13142     /**
13143      * Sorts this collection with the passed comparison function
13144      * @param {String} direction (optional) "ASC" or "DESC"
13145      * @param {Function} fn (optional) comparison function
13146      */
13147     sort : function(dir, fn){
13148         this._sort("value", dir, fn);
13149     },
13150     
13151     /**
13152      * Sorts this collection by keys
13153      * @param {String} direction (optional) "ASC" or "DESC"
13154      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13155      */
13156     keySort : function(dir, fn){
13157         this._sort("key", dir, fn || function(a, b){
13158             return String(a).toUpperCase()-String(b).toUpperCase();
13159         });
13160     },
13161     
13162     /**
13163      * Returns a range of items in this collection
13164      * @param {Number} startIndex (optional) defaults to 0
13165      * @param {Number} endIndex (optional) default to the last item
13166      * @return {Array} An array of items
13167      */
13168     getRange : function(start, end){
13169         var items = this.items;
13170         if(items.length < 1){
13171             return [];
13172         }
13173         start = start || 0;
13174         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13175         var r = [];
13176         if(start <= end){
13177             for(var i = start; i <= end; i++) {
13178                     r[r.length] = items[i];
13179             }
13180         }else{
13181             for(var i = start; i >= end; i--) {
13182                     r[r.length] = items[i];
13183             }
13184         }
13185         return r;
13186     },
13187         
13188     /**
13189      * Filter the <i>objects</i> in this collection by a specific property. 
13190      * Returns a new collection that has been filtered.
13191      * @param {String} property A property on your objects
13192      * @param {String/RegExp} value Either string that the property values 
13193      * should start with or a RegExp to test against the property
13194      * @return {MixedCollection} The new filtered collection
13195      */
13196     filter : function(property, value){
13197         if(!value.exec){ // not a regex
13198             value = String(value);
13199             if(value.length == 0){
13200                 return this.clone();
13201             }
13202             value = new RegExp("^" + Roo.escapeRe(value), "i");
13203         }
13204         return this.filterBy(function(o){
13205             return o && value.test(o[property]);
13206         });
13207         },
13208     
13209     /**
13210      * Filter by a function. * Returns a new collection that has been filtered.
13211      * The passed function will be called with each 
13212      * object in the collection. If the function returns true, the value is included 
13213      * otherwise it is filtered.
13214      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13215      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13216      * @return {MixedCollection} The new filtered collection
13217      */
13218     filterBy : function(fn, scope){
13219         var r = new Roo.util.MixedCollection();
13220         r.getKey = this.getKey;
13221         var k = this.keys, it = this.items;
13222         for(var i = 0, len = it.length; i < len; i++){
13223             if(fn.call(scope||this, it[i], k[i])){
13224                                 r.add(k[i], it[i]);
13225                         }
13226         }
13227         return r;
13228     },
13229     
13230     /**
13231      * Creates a duplicate of this collection
13232      * @return {MixedCollection}
13233      */
13234     clone : function(){
13235         var r = new Roo.util.MixedCollection();
13236         var k = this.keys, it = this.items;
13237         for(var i = 0, len = it.length; i < len; i++){
13238             r.add(k[i], it[i]);
13239         }
13240         r.getKey = this.getKey;
13241         return r;
13242     }
13243 });
13244 /**
13245  * Returns the item associated with the passed key or index.
13246  * @method
13247  * @param {String/Number} key The key or index of the item.
13248  * @return {Object} The item associated with the passed key.
13249  */
13250 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13251  * Based on:
13252  * Ext JS Library 1.1.1
13253  * Copyright(c) 2006-2007, Ext JS, LLC.
13254  *
13255  * Originally Released Under LGPL - original licence link has changed is not relivant.
13256  *
13257  * Fork - LGPL
13258  * <script type="text/javascript">
13259  */
13260 /**
13261  * @class Roo.util.JSON
13262  * Modified version of Douglas Crockford"s json.js that doesn"t
13263  * mess with the Object prototype 
13264  * http://www.json.org/js.html
13265  * @singleton
13266  */
13267 Roo.util.JSON = new (function(){
13268     var useHasOwn = {}.hasOwnProperty ? true : false;
13269     
13270     // crashes Safari in some instances
13271     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13272     
13273     var pad = function(n) {
13274         return n < 10 ? "0" + n : n;
13275     };
13276     
13277     var m = {
13278         "\b": '\\b',
13279         "\t": '\\t',
13280         "\n": '\\n',
13281         "\f": '\\f',
13282         "\r": '\\r',
13283         '"' : '\\"',
13284         "\\": '\\\\'
13285     };
13286
13287     var encodeString = function(s){
13288         if (/["\\\x00-\x1f]/.test(s)) {
13289             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13290                 var c = m[b];
13291                 if(c){
13292                     return c;
13293                 }
13294                 c = b.charCodeAt();
13295                 return "\\u00" +
13296                     Math.floor(c / 16).toString(16) +
13297                     (c % 16).toString(16);
13298             }) + '"';
13299         }
13300         return '"' + s + '"';
13301     };
13302     
13303     var encodeArray = function(o){
13304         var a = ["["], b, i, l = o.length, v;
13305             for (i = 0; i < l; i += 1) {
13306                 v = o[i];
13307                 switch (typeof v) {
13308                     case "undefined":
13309                     case "function":
13310                     case "unknown":
13311                         break;
13312                     default:
13313                         if (b) {
13314                             a.push(',');
13315                         }
13316                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13317                         b = true;
13318                 }
13319             }
13320             a.push("]");
13321             return a.join("");
13322     };
13323     
13324     var encodeDate = function(o){
13325         return '"' + o.getFullYear() + "-" +
13326                 pad(o.getMonth() + 1) + "-" +
13327                 pad(o.getDate()) + "T" +
13328                 pad(o.getHours()) + ":" +
13329                 pad(o.getMinutes()) + ":" +
13330                 pad(o.getSeconds()) + '"';
13331     };
13332     
13333     /**
13334      * Encodes an Object, Array or other value
13335      * @param {Mixed} o The variable to encode
13336      * @return {String} The JSON string
13337      */
13338     this.encode = function(o)
13339     {
13340         // should this be extended to fully wrap stringify..
13341         
13342         if(typeof o == "undefined" || o === null){
13343             return "null";
13344         }else if(o instanceof Array){
13345             return encodeArray(o);
13346         }else if(o instanceof Date){
13347             return encodeDate(o);
13348         }else if(typeof o == "string"){
13349             return encodeString(o);
13350         }else if(typeof o == "number"){
13351             return isFinite(o) ? String(o) : "null";
13352         }else if(typeof o == "boolean"){
13353             return String(o);
13354         }else {
13355             var a = ["{"], b, i, v;
13356             for (i in o) {
13357                 if(!useHasOwn || o.hasOwnProperty(i)) {
13358                     v = o[i];
13359                     switch (typeof v) {
13360                     case "undefined":
13361                     case "function":
13362                     case "unknown":
13363                         break;
13364                     default:
13365                         if(b){
13366                             a.push(',');
13367                         }
13368                         a.push(this.encode(i), ":",
13369                                 v === null ? "null" : this.encode(v));
13370                         b = true;
13371                     }
13372                 }
13373             }
13374             a.push("}");
13375             return a.join("");
13376         }
13377     };
13378     
13379     /**
13380      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13381      * @param {String} json The JSON string
13382      * @return {Object} The resulting object
13383      */
13384     this.decode = function(json){
13385         
13386         return  /** eval:var:json */ eval("(" + json + ')');
13387     };
13388 })();
13389 /** 
13390  * Shorthand for {@link Roo.util.JSON#encode}
13391  * @member Roo encode 
13392  * @method */
13393 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13394 /** 
13395  * Shorthand for {@link Roo.util.JSON#decode}
13396  * @member Roo decode 
13397  * @method */
13398 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13399 /*
13400  * Based on:
13401  * Ext JS Library 1.1.1
13402  * Copyright(c) 2006-2007, Ext JS, LLC.
13403  *
13404  * Originally Released Under LGPL - original licence link has changed is not relivant.
13405  *
13406  * Fork - LGPL
13407  * <script type="text/javascript">
13408  */
13409  
13410 /**
13411  * @class Roo.util.Format
13412  * Reusable data formatting functions
13413  * @singleton
13414  */
13415 Roo.util.Format = function(){
13416     var trimRe = /^\s+|\s+$/g;
13417     return {
13418         /**
13419          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13420          * @param {String} value The string to truncate
13421          * @param {Number} length The maximum length to allow before truncating
13422          * @return {String} The converted text
13423          */
13424         ellipsis : function(value, len){
13425             if(value && value.length > len){
13426                 return value.substr(0, len-3)+"...";
13427             }
13428             return value;
13429         },
13430
13431         /**
13432          * Checks a reference and converts it to empty string if it is undefined
13433          * @param {Mixed} value Reference to check
13434          * @return {Mixed} Empty string if converted, otherwise the original value
13435          */
13436         undef : function(value){
13437             return typeof value != "undefined" ? value : "";
13438         },
13439
13440         /**
13441          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13442          * @param {String} value The string to encode
13443          * @return {String} The encoded text
13444          */
13445         htmlEncode : function(value){
13446             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13447         },
13448
13449         /**
13450          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13451          * @param {String} value The string to decode
13452          * @return {String} The decoded text
13453          */
13454         htmlDecode : function(value){
13455             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13456         },
13457
13458         /**
13459          * Trims any whitespace from either side of a string
13460          * @param {String} value The text to trim
13461          * @return {String} The trimmed text
13462          */
13463         trim : function(value){
13464             return String(value).replace(trimRe, "");
13465         },
13466
13467         /**
13468          * Returns a substring from within an original string
13469          * @param {String} value The original text
13470          * @param {Number} start The start index of the substring
13471          * @param {Number} length The length of the substring
13472          * @return {String} The substring
13473          */
13474         substr : function(value, start, length){
13475             return String(value).substr(start, length);
13476         },
13477
13478         /**
13479          * Converts a string to all lower case letters
13480          * @param {String} value The text to convert
13481          * @return {String} The converted text
13482          */
13483         lowercase : function(value){
13484             return String(value).toLowerCase();
13485         },
13486
13487         /**
13488          * Converts a string to all upper case letters
13489          * @param {String} value The text to convert
13490          * @return {String} The converted text
13491          */
13492         uppercase : function(value){
13493             return String(value).toUpperCase();
13494         },
13495
13496         /**
13497          * Converts the first character only of a string to upper case
13498          * @param {String} value The text to convert
13499          * @return {String} The converted text
13500          */
13501         capitalize : function(value){
13502             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13503         },
13504
13505         // private
13506         call : function(value, fn){
13507             if(arguments.length > 2){
13508                 var args = Array.prototype.slice.call(arguments, 2);
13509                 args.unshift(value);
13510                  
13511                 return /** eval:var:value */  eval(fn).apply(window, args);
13512             }else{
13513                 /** eval:var:value */
13514                 return /** eval:var:value */ eval(fn).call(window, value);
13515             }
13516         },
13517
13518        
13519         /**
13520          * safer version of Math.toFixed..??/
13521          * @param {Number/String} value The numeric value to format
13522          * @param {Number/String} value Decimal places 
13523          * @return {String} The formatted currency string
13524          */
13525         toFixed : function(v, n)
13526         {
13527             // why not use to fixed - precision is buggered???
13528             if (!n) {
13529                 return Math.round(v-0);
13530             }
13531             var fact = Math.pow(10,n+1);
13532             v = (Math.round((v-0)*fact))/fact;
13533             var z = (''+fact).substring(2);
13534             if (v == Math.floor(v)) {
13535                 return Math.floor(v) + '.' + z;
13536             }
13537             
13538             // now just padd decimals..
13539             var ps = String(v).split('.');
13540             var fd = (ps[1] + z);
13541             var r = fd.substring(0,n); 
13542             var rm = fd.substring(n); 
13543             if (rm < 5) {
13544                 return ps[0] + '.' + r;
13545             }
13546             r*=1; // turn it into a number;
13547             r++;
13548             if (String(r).length != n) {
13549                 ps[0]*=1;
13550                 ps[0]++;
13551                 r = String(r).substring(1); // chop the end off.
13552             }
13553             
13554             return ps[0] + '.' + r;
13555              
13556         },
13557         
13558         /**
13559          * Format a number as US currency
13560          * @param {Number/String} value The numeric value to format
13561          * @return {String} The formatted currency string
13562          */
13563         usMoney : function(v){
13564             return '$' + Roo.util.Format.number(v);
13565         },
13566         
13567         /**
13568          * Format a number
13569          * eventually this should probably emulate php's number_format
13570          * @param {Number/String} value The numeric value to format
13571          * @param {Number} decimals number of decimal places
13572          * @return {String} The formatted currency string
13573          */
13574         number : function(v,decimals)
13575         {
13576             // multiply and round.
13577             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13578             var mul = Math.pow(10, decimals);
13579             var zero = String(mul).substring(1);
13580             v = (Math.round((v-0)*mul))/mul;
13581             
13582             // if it's '0' number.. then
13583             
13584             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13585             v = String(v);
13586             var ps = v.split('.');
13587             var whole = ps[0];
13588             
13589             
13590             var r = /(\d+)(\d{3})/;
13591             // add comma's
13592             while (r.test(whole)) {
13593                 whole = whole.replace(r, '$1' + ',' + '$2');
13594             }
13595             
13596             
13597             var sub = ps[1] ?
13598                     // has decimals..
13599                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13600                     // does not have decimals
13601                     (decimals ? ('.' + zero) : '');
13602             
13603             
13604             return whole + sub ;
13605         },
13606         
13607         /**
13608          * Parse a value into a formatted date using the specified format pattern.
13609          * @param {Mixed} value The value to format
13610          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13611          * @return {String} The formatted date string
13612          */
13613         date : function(v, format){
13614             if(!v){
13615                 return "";
13616             }
13617             if(!(v instanceof Date)){
13618                 v = new Date(Date.parse(v));
13619             }
13620             return v.dateFormat(format || Roo.util.Format.defaults.date);
13621         },
13622
13623         /**
13624          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13625          * @param {String} format Any valid date format string
13626          * @return {Function} The date formatting function
13627          */
13628         dateRenderer : function(format){
13629             return function(v){
13630                 return Roo.util.Format.date(v, format);  
13631             };
13632         },
13633
13634         // private
13635         stripTagsRE : /<\/?[^>]+>/gi,
13636         
13637         /**
13638          * Strips all HTML tags
13639          * @param {Mixed} value The text from which to strip tags
13640          * @return {String} The stripped text
13641          */
13642         stripTags : function(v){
13643             return !v ? v : String(v).replace(this.stripTagsRE, "");
13644         }
13645     };
13646 }();
13647 Roo.util.Format.defaults = {
13648     date : 'd/M/Y'
13649 };/*
13650  * Based on:
13651  * Ext JS Library 1.1.1
13652  * Copyright(c) 2006-2007, Ext JS, LLC.
13653  *
13654  * Originally Released Under LGPL - original licence link has changed is not relivant.
13655  *
13656  * Fork - LGPL
13657  * <script type="text/javascript">
13658  */
13659
13660
13661  
13662
13663 /**
13664  * @class Roo.MasterTemplate
13665  * @extends Roo.Template
13666  * Provides a template that can have child templates. The syntax is:
13667 <pre><code>
13668 var t = new Roo.MasterTemplate(
13669         '&lt;select name="{name}"&gt;',
13670                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13671         '&lt;/select&gt;'
13672 );
13673 t.add('options', {value: 'foo', text: 'bar'});
13674 // or you can add multiple child elements in one shot
13675 t.addAll('options', [
13676     {value: 'foo', text: 'bar'},
13677     {value: 'foo2', text: 'bar2'},
13678     {value: 'foo3', text: 'bar3'}
13679 ]);
13680 // then append, applying the master template values
13681 t.append('my-form', {name: 'my-select'});
13682 </code></pre>
13683 * A name attribute for the child template is not required if you have only one child
13684 * template or you want to refer to them by index.
13685  */
13686 Roo.MasterTemplate = function(){
13687     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13688     this.originalHtml = this.html;
13689     var st = {};
13690     var m, re = this.subTemplateRe;
13691     re.lastIndex = 0;
13692     var subIndex = 0;
13693     while(m = re.exec(this.html)){
13694         var name = m[1], content = m[2];
13695         st[subIndex] = {
13696             name: name,
13697             index: subIndex,
13698             buffer: [],
13699             tpl : new Roo.Template(content)
13700         };
13701         if(name){
13702             st[name] = st[subIndex];
13703         }
13704         st[subIndex].tpl.compile();
13705         st[subIndex].tpl.call = this.call.createDelegate(this);
13706         subIndex++;
13707     }
13708     this.subCount = subIndex;
13709     this.subs = st;
13710 };
13711 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13712     /**
13713     * The regular expression used to match sub templates
13714     * @type RegExp
13715     * @property
13716     */
13717     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13718
13719     /**
13720      * Applies the passed values to a child template.
13721      * @param {String/Number} name (optional) The name or index of the child template
13722      * @param {Array/Object} values The values to be applied to the template
13723      * @return {MasterTemplate} this
13724      */
13725      add : function(name, values){
13726         if(arguments.length == 1){
13727             values = arguments[0];
13728             name = 0;
13729         }
13730         var s = this.subs[name];
13731         s.buffer[s.buffer.length] = s.tpl.apply(values);
13732         return this;
13733     },
13734
13735     /**
13736      * Applies all the passed values to a child template.
13737      * @param {String/Number} name (optional) The name or index of the child template
13738      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13739      * @param {Boolean} reset (optional) True to reset the template first
13740      * @return {MasterTemplate} this
13741      */
13742     fill : function(name, values, reset){
13743         var a = arguments;
13744         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13745             values = a[0];
13746             name = 0;
13747             reset = a[1];
13748         }
13749         if(reset){
13750             this.reset();
13751         }
13752         for(var i = 0, len = values.length; i < len; i++){
13753             this.add(name, values[i]);
13754         }
13755         return this;
13756     },
13757
13758     /**
13759      * Resets the template for reuse
13760      * @return {MasterTemplate} this
13761      */
13762      reset : function(){
13763         var s = this.subs;
13764         for(var i = 0; i < this.subCount; i++){
13765             s[i].buffer = [];
13766         }
13767         return this;
13768     },
13769
13770     applyTemplate : function(values){
13771         var s = this.subs;
13772         var replaceIndex = -1;
13773         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13774             return s[++replaceIndex].buffer.join("");
13775         });
13776         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13777     },
13778
13779     apply : function(){
13780         return this.applyTemplate.apply(this, arguments);
13781     },
13782
13783     compile : function(){return this;}
13784 });
13785
13786 /**
13787  * Alias for fill().
13788  * @method
13789  */
13790 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13791  /**
13792  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13793  * var tpl = Roo.MasterTemplate.from('element-id');
13794  * @param {String/HTMLElement} el
13795  * @param {Object} config
13796  * @static
13797  */
13798 Roo.MasterTemplate.from = function(el, config){
13799     el = Roo.getDom(el);
13800     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13801 };/*
13802  * Based on:
13803  * Ext JS Library 1.1.1
13804  * Copyright(c) 2006-2007, Ext JS, LLC.
13805  *
13806  * Originally Released Under LGPL - original licence link has changed is not relivant.
13807  *
13808  * Fork - LGPL
13809  * <script type="text/javascript">
13810  */
13811
13812  
13813 /**
13814  * @class Roo.util.CSS
13815  * Utility class for manipulating CSS rules
13816  * @singleton
13817  */
13818 Roo.util.CSS = function(){
13819         var rules = null;
13820         var doc = document;
13821
13822     var camelRe = /(-[a-z])/gi;
13823     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13824
13825    return {
13826    /**
13827     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13828     * tag and appended to the HEAD of the document.
13829     * @param {String|Object} cssText The text containing the css rules
13830     * @param {String} id An id to add to the stylesheet for later removal
13831     * @return {StyleSheet}
13832     */
13833     createStyleSheet : function(cssText, id){
13834         var ss;
13835         var head = doc.getElementsByTagName("head")[0];
13836         var nrules = doc.createElement("style");
13837         nrules.setAttribute("type", "text/css");
13838         if(id){
13839             nrules.setAttribute("id", id);
13840         }
13841         if (typeof(cssText) != 'string') {
13842             // support object maps..
13843             // not sure if this a good idea.. 
13844             // perhaps it should be merged with the general css handling
13845             // and handle js style props.
13846             var cssTextNew = [];
13847             for(var n in cssText) {
13848                 var citems = [];
13849                 for(var k in cssText[n]) {
13850                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13851                 }
13852                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13853                 
13854             }
13855             cssText = cssTextNew.join("\n");
13856             
13857         }
13858        
13859        
13860        if(Roo.isIE){
13861            head.appendChild(nrules);
13862            ss = nrules.styleSheet;
13863            ss.cssText = cssText;
13864        }else{
13865            try{
13866                 nrules.appendChild(doc.createTextNode(cssText));
13867            }catch(e){
13868                nrules.cssText = cssText; 
13869            }
13870            head.appendChild(nrules);
13871            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13872        }
13873        this.cacheStyleSheet(ss);
13874        return ss;
13875    },
13876
13877    /**
13878     * Removes a style or link tag by id
13879     * @param {String} id The id of the tag
13880     */
13881    removeStyleSheet : function(id){
13882        var existing = doc.getElementById(id);
13883        if(existing){
13884            existing.parentNode.removeChild(existing);
13885        }
13886    },
13887
13888    /**
13889     * Dynamically swaps an existing stylesheet reference for a new one
13890     * @param {String} id The id of an existing link tag to remove
13891     * @param {String} url The href of the new stylesheet to include
13892     */
13893    swapStyleSheet : function(id, url){
13894        this.removeStyleSheet(id);
13895        var ss = doc.createElement("link");
13896        ss.setAttribute("rel", "stylesheet");
13897        ss.setAttribute("type", "text/css");
13898        ss.setAttribute("id", id);
13899        ss.setAttribute("href", url);
13900        doc.getElementsByTagName("head")[0].appendChild(ss);
13901    },
13902    
13903    /**
13904     * Refresh the rule cache if you have dynamically added stylesheets
13905     * @return {Object} An object (hash) of rules indexed by selector
13906     */
13907    refreshCache : function(){
13908        return this.getRules(true);
13909    },
13910
13911    // private
13912    cacheStyleSheet : function(stylesheet){
13913        if(!rules){
13914            rules = {};
13915        }
13916        try{// try catch for cross domain access issue
13917            var ssRules = stylesheet.cssRules || stylesheet.rules;
13918            for(var j = ssRules.length-1; j >= 0; --j){
13919                rules[ssRules[j].selectorText] = ssRules[j];
13920            }
13921        }catch(e){}
13922    },
13923    
13924    /**
13925     * Gets all css rules for the document
13926     * @param {Boolean} refreshCache true to refresh the internal cache
13927     * @return {Object} An object (hash) of rules indexed by selector
13928     */
13929    getRules : function(refreshCache){
13930                 if(rules == null || refreshCache){
13931                         rules = {};
13932                         var ds = doc.styleSheets;
13933                         for(var i =0, len = ds.length; i < len; i++){
13934                             try{
13935                         this.cacheStyleSheet(ds[i]);
13936                     }catch(e){} 
13937                 }
13938                 }
13939                 return rules;
13940         },
13941         
13942         /**
13943     * Gets an an individual CSS rule by selector(s)
13944     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13945     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13946     * @return {CSSRule} The CSS rule or null if one is not found
13947     */
13948    getRule : function(selector, refreshCache){
13949                 var rs = this.getRules(refreshCache);
13950                 if(!(selector instanceof Array)){
13951                     return rs[selector];
13952                 }
13953                 for(var i = 0; i < selector.length; i++){
13954                         if(rs[selector[i]]){
13955                                 return rs[selector[i]];
13956                         }
13957                 }
13958                 return null;
13959         },
13960         
13961         
13962         /**
13963     * Updates a rule property
13964     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13965     * @param {String} property The css property
13966     * @param {String} value The new value for the property
13967     * @return {Boolean} true If a rule was found and updated
13968     */
13969    updateRule : function(selector, property, value){
13970                 if(!(selector instanceof Array)){
13971                         var rule = this.getRule(selector);
13972                         if(rule){
13973                                 rule.style[property.replace(camelRe, camelFn)] = value;
13974                                 return true;
13975                         }
13976                 }else{
13977                         for(var i = 0; i < selector.length; i++){
13978                                 if(this.updateRule(selector[i], property, value)){
13979                                         return true;
13980                                 }
13981                         }
13982                 }
13983                 return false;
13984         }
13985    };   
13986 }();/*
13987  * Based on:
13988  * Ext JS Library 1.1.1
13989  * Copyright(c) 2006-2007, Ext JS, LLC.
13990  *
13991  * Originally Released Under LGPL - original licence link has changed is not relivant.
13992  *
13993  * Fork - LGPL
13994  * <script type="text/javascript">
13995  */
13996
13997  
13998
13999 /**
14000  * @class Roo.util.ClickRepeater
14001  * @extends Roo.util.Observable
14002  * 
14003  * A wrapper class which can be applied to any element. Fires a "click" event while the
14004  * mouse is pressed. The interval between firings may be specified in the config but
14005  * defaults to 10 milliseconds.
14006  * 
14007  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14008  * 
14009  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14010  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14011  * Similar to an autorepeat key delay.
14012  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14013  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14014  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14015  *           "interval" and "delay" are ignored. "immediate" is honored.
14016  * @cfg {Boolean} preventDefault True to prevent the default click event
14017  * @cfg {Boolean} stopDefault True to stop the default click event
14018  * 
14019  * @history
14020  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14021  *     2007-02-02 jvs Renamed to ClickRepeater
14022  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14023  *
14024  *  @constructor
14025  * @param {String/HTMLElement/Element} el The element to listen on
14026  * @param {Object} config
14027  **/
14028 Roo.util.ClickRepeater = function(el, config)
14029 {
14030     this.el = Roo.get(el);
14031     this.el.unselectable();
14032
14033     Roo.apply(this, config);
14034
14035     this.addEvents({
14036     /**
14037      * @event mousedown
14038      * Fires when the mouse button is depressed.
14039      * @param {Roo.util.ClickRepeater} this
14040      */
14041         "mousedown" : true,
14042     /**
14043      * @event click
14044      * Fires on a specified interval during the time the element is pressed.
14045      * @param {Roo.util.ClickRepeater} this
14046      */
14047         "click" : true,
14048     /**
14049      * @event mouseup
14050      * Fires when the mouse key is released.
14051      * @param {Roo.util.ClickRepeater} this
14052      */
14053         "mouseup" : true
14054     });
14055
14056     this.el.on("mousedown", this.handleMouseDown, this);
14057     if(this.preventDefault || this.stopDefault){
14058         this.el.on("click", function(e){
14059             if(this.preventDefault){
14060                 e.preventDefault();
14061             }
14062             if(this.stopDefault){
14063                 e.stopEvent();
14064             }
14065         }, this);
14066     }
14067
14068     // allow inline handler
14069     if(this.handler){
14070         this.on("click", this.handler,  this.scope || this);
14071     }
14072
14073     Roo.util.ClickRepeater.superclass.constructor.call(this);
14074 };
14075
14076 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14077     interval : 20,
14078     delay: 250,
14079     preventDefault : true,
14080     stopDefault : false,
14081     timer : 0,
14082
14083     // private
14084     handleMouseDown : function(){
14085         clearTimeout(this.timer);
14086         this.el.blur();
14087         if(this.pressClass){
14088             this.el.addClass(this.pressClass);
14089         }
14090         this.mousedownTime = new Date();
14091
14092         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14093         this.el.on("mouseout", this.handleMouseOut, this);
14094
14095         this.fireEvent("mousedown", this);
14096         this.fireEvent("click", this);
14097         
14098         this.timer = this.click.defer(this.delay || this.interval, this);
14099     },
14100
14101     // private
14102     click : function(){
14103         this.fireEvent("click", this);
14104         this.timer = this.click.defer(this.getInterval(), this);
14105     },
14106
14107     // private
14108     getInterval: function(){
14109         if(!this.accelerate){
14110             return this.interval;
14111         }
14112         var pressTime = this.mousedownTime.getElapsed();
14113         if(pressTime < 500){
14114             return 400;
14115         }else if(pressTime < 1700){
14116             return 320;
14117         }else if(pressTime < 2600){
14118             return 250;
14119         }else if(pressTime < 3500){
14120             return 180;
14121         }else if(pressTime < 4400){
14122             return 140;
14123         }else if(pressTime < 5300){
14124             return 80;
14125         }else if(pressTime < 6200){
14126             return 50;
14127         }else{
14128             return 10;
14129         }
14130     },
14131
14132     // private
14133     handleMouseOut : function(){
14134         clearTimeout(this.timer);
14135         if(this.pressClass){
14136             this.el.removeClass(this.pressClass);
14137         }
14138         this.el.on("mouseover", this.handleMouseReturn, this);
14139     },
14140
14141     // private
14142     handleMouseReturn : function(){
14143         this.el.un("mouseover", this.handleMouseReturn);
14144         if(this.pressClass){
14145             this.el.addClass(this.pressClass);
14146         }
14147         this.click();
14148     },
14149
14150     // private
14151     handleMouseUp : function(){
14152         clearTimeout(this.timer);
14153         this.el.un("mouseover", this.handleMouseReturn);
14154         this.el.un("mouseout", this.handleMouseOut);
14155         Roo.get(document).un("mouseup", this.handleMouseUp);
14156         this.el.removeClass(this.pressClass);
14157         this.fireEvent("mouseup", this);
14158     }
14159 });/*
14160  * Based on:
14161  * Ext JS Library 1.1.1
14162  * Copyright(c) 2006-2007, Ext JS, LLC.
14163  *
14164  * Originally Released Under LGPL - original licence link has changed is not relivant.
14165  *
14166  * Fork - LGPL
14167  * <script type="text/javascript">
14168  */
14169
14170  
14171 /**
14172  * @class Roo.KeyNav
14173  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14174  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14175  * way to implement custom navigation schemes for any UI component.</p>
14176  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14177  * pageUp, pageDown, del, home, end.  Usage:</p>
14178  <pre><code>
14179 var nav = new Roo.KeyNav("my-element", {
14180     "left" : function(e){
14181         this.moveLeft(e.ctrlKey);
14182     },
14183     "right" : function(e){
14184         this.moveRight(e.ctrlKey);
14185     },
14186     "enter" : function(e){
14187         this.save();
14188     },
14189     scope : this
14190 });
14191 </code></pre>
14192  * @constructor
14193  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14194  * @param {Object} config The config
14195  */
14196 Roo.KeyNav = function(el, config){
14197     this.el = Roo.get(el);
14198     Roo.apply(this, config);
14199     if(!this.disabled){
14200         this.disabled = true;
14201         this.enable();
14202     }
14203 };
14204
14205 Roo.KeyNav.prototype = {
14206     /**
14207      * @cfg {Boolean} disabled
14208      * True to disable this KeyNav instance (defaults to false)
14209      */
14210     disabled : false,
14211     /**
14212      * @cfg {String} defaultEventAction
14213      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14214      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14215      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14216      */
14217     defaultEventAction: "stopEvent",
14218     /**
14219      * @cfg {Boolean} forceKeyDown
14220      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14221      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14222      * handle keydown instead of keypress.
14223      */
14224     forceKeyDown : false,
14225
14226     // private
14227     prepareEvent : function(e){
14228         var k = e.getKey();
14229         var h = this.keyToHandler[k];
14230         //if(h && this[h]){
14231         //    e.stopPropagation();
14232         //}
14233         if(Roo.isSafari && h && k >= 37 && k <= 40){
14234             e.stopEvent();
14235         }
14236     },
14237
14238     // private
14239     relay : function(e){
14240         var k = e.getKey();
14241         var h = this.keyToHandler[k];
14242         if(h && this[h]){
14243             if(this.doRelay(e, this[h], h) !== true){
14244                 e[this.defaultEventAction]();
14245             }
14246         }
14247     },
14248
14249     // private
14250     doRelay : function(e, h, hname){
14251         return h.call(this.scope || this, e);
14252     },
14253
14254     // possible handlers
14255     enter : false,
14256     left : false,
14257     right : false,
14258     up : false,
14259     down : false,
14260     tab : false,
14261     esc : false,
14262     pageUp : false,
14263     pageDown : false,
14264     del : false,
14265     home : false,
14266     end : false,
14267
14268     // quick lookup hash
14269     keyToHandler : {
14270         37 : "left",
14271         39 : "right",
14272         38 : "up",
14273         40 : "down",
14274         33 : "pageUp",
14275         34 : "pageDown",
14276         46 : "del",
14277         36 : "home",
14278         35 : "end",
14279         13 : "enter",
14280         27 : "esc",
14281         9  : "tab"
14282     },
14283
14284         /**
14285          * Enable this KeyNav
14286          */
14287         enable: function(){
14288                 if(this.disabled){
14289             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14290             // the EventObject will normalize Safari automatically
14291             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14292                 this.el.on("keydown", this.relay,  this);
14293             }else{
14294                 this.el.on("keydown", this.prepareEvent,  this);
14295                 this.el.on("keypress", this.relay,  this);
14296             }
14297                     this.disabled = false;
14298                 }
14299         },
14300
14301         /**
14302          * Disable this KeyNav
14303          */
14304         disable: function(){
14305                 if(!this.disabled){
14306                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14307                 this.el.un("keydown", this.relay);
14308             }else{
14309                 this.el.un("keydown", this.prepareEvent);
14310                 this.el.un("keypress", this.relay);
14311             }
14312                     this.disabled = true;
14313                 }
14314         }
14315 };/*
14316  * Based on:
14317  * Ext JS Library 1.1.1
14318  * Copyright(c) 2006-2007, Ext JS, LLC.
14319  *
14320  * Originally Released Under LGPL - original licence link has changed is not relivant.
14321  *
14322  * Fork - LGPL
14323  * <script type="text/javascript">
14324  */
14325
14326  
14327 /**
14328  * @class Roo.KeyMap
14329  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14330  * The constructor accepts the same config object as defined by {@link #addBinding}.
14331  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14332  * combination it will call the function with this signature (if the match is a multi-key
14333  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14334  * A KeyMap can also handle a string representation of keys.<br />
14335  * Usage:
14336  <pre><code>
14337 // map one key by key code
14338 var map = new Roo.KeyMap("my-element", {
14339     key: 13, // or Roo.EventObject.ENTER
14340     fn: myHandler,
14341     scope: myObject
14342 });
14343
14344 // map multiple keys to one action by string
14345 var map = new Roo.KeyMap("my-element", {
14346     key: "a\r\n\t",
14347     fn: myHandler,
14348     scope: myObject
14349 });
14350
14351 // map multiple keys to multiple actions by strings and array of codes
14352 var map = new Roo.KeyMap("my-element", [
14353     {
14354         key: [10,13],
14355         fn: function(){ alert("Return was pressed"); }
14356     }, {
14357         key: "abc",
14358         fn: function(){ alert('a, b or c was pressed'); }
14359     }, {
14360         key: "\t",
14361         ctrl:true,
14362         shift:true,
14363         fn: function(){ alert('Control + shift + tab was pressed.'); }
14364     }
14365 ]);
14366 </code></pre>
14367  * <b>Note: A KeyMap starts enabled</b>
14368  * @constructor
14369  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14370  * @param {Object} config The config (see {@link #addBinding})
14371  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14372  */
14373 Roo.KeyMap = function(el, config, eventName){
14374     this.el  = Roo.get(el);
14375     this.eventName = eventName || "keydown";
14376     this.bindings = [];
14377     if(config){
14378         this.addBinding(config);
14379     }
14380     this.enable();
14381 };
14382
14383 Roo.KeyMap.prototype = {
14384     /**
14385      * True to stop the event from bubbling and prevent the default browser action if the
14386      * key was handled by the KeyMap (defaults to false)
14387      * @type Boolean
14388      */
14389     stopEvent : false,
14390
14391     /**
14392      * Add a new binding to this KeyMap. The following config object properties are supported:
14393      * <pre>
14394 Property    Type             Description
14395 ----------  ---------------  ----------------------------------------------------------------------
14396 key         String/Array     A single keycode or an array of keycodes to handle
14397 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14398 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14399 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14400 fn          Function         The function to call when KeyMap finds the expected key combination
14401 scope       Object           The scope of the callback function
14402 </pre>
14403      *
14404      * Usage:
14405      * <pre><code>
14406 // Create a KeyMap
14407 var map = new Roo.KeyMap(document, {
14408     key: Roo.EventObject.ENTER,
14409     fn: handleKey,
14410     scope: this
14411 });
14412
14413 //Add a new binding to the existing KeyMap later
14414 map.addBinding({
14415     key: 'abc',
14416     shift: true,
14417     fn: handleKey,
14418     scope: this
14419 });
14420 </code></pre>
14421      * @param {Object/Array} config A single KeyMap config or an array of configs
14422      */
14423         addBinding : function(config){
14424         if(config instanceof Array){
14425             for(var i = 0, len = config.length; i < len; i++){
14426                 this.addBinding(config[i]);
14427             }
14428             return;
14429         }
14430         var keyCode = config.key,
14431             shift = config.shift, 
14432             ctrl = config.ctrl, 
14433             alt = config.alt,
14434             fn = config.fn,
14435             scope = config.scope;
14436         if(typeof keyCode == "string"){
14437             var ks = [];
14438             var keyString = keyCode.toUpperCase();
14439             for(var j = 0, len = keyString.length; j < len; j++){
14440                 ks.push(keyString.charCodeAt(j));
14441             }
14442             keyCode = ks;
14443         }
14444         var keyArray = keyCode instanceof Array;
14445         var handler = function(e){
14446             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14447                 var k = e.getKey();
14448                 if(keyArray){
14449                     for(var i = 0, len = keyCode.length; i < len; i++){
14450                         if(keyCode[i] == k){
14451                           if(this.stopEvent){
14452                               e.stopEvent();
14453                           }
14454                           fn.call(scope || window, k, e);
14455                           return;
14456                         }
14457                     }
14458                 }else{
14459                     if(k == keyCode){
14460                         if(this.stopEvent){
14461                            e.stopEvent();
14462                         }
14463                         fn.call(scope || window, k, e);
14464                     }
14465                 }
14466             }
14467         };
14468         this.bindings.push(handler);  
14469         },
14470
14471     /**
14472      * Shorthand for adding a single key listener
14473      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14474      * following options:
14475      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14476      * @param {Function} fn The function to call
14477      * @param {Object} scope (optional) The scope of the function
14478      */
14479     on : function(key, fn, scope){
14480         var keyCode, shift, ctrl, alt;
14481         if(typeof key == "object" && !(key instanceof Array)){
14482             keyCode = key.key;
14483             shift = key.shift;
14484             ctrl = key.ctrl;
14485             alt = key.alt;
14486         }else{
14487             keyCode = key;
14488         }
14489         this.addBinding({
14490             key: keyCode,
14491             shift: shift,
14492             ctrl: ctrl,
14493             alt: alt,
14494             fn: fn,
14495             scope: scope
14496         })
14497     },
14498
14499     // private
14500     handleKeyDown : function(e){
14501             if(this.enabled){ //just in case
14502             var b = this.bindings;
14503             for(var i = 0, len = b.length; i < len; i++){
14504                 b[i].call(this, e);
14505             }
14506             }
14507         },
14508         
14509         /**
14510          * Returns true if this KeyMap is enabled
14511          * @return {Boolean} 
14512          */
14513         isEnabled : function(){
14514             return this.enabled;  
14515         },
14516         
14517         /**
14518          * Enables this KeyMap
14519          */
14520         enable: function(){
14521                 if(!this.enabled){
14522                     this.el.on(this.eventName, this.handleKeyDown, this);
14523                     this.enabled = true;
14524                 }
14525         },
14526
14527         /**
14528          * Disable this KeyMap
14529          */
14530         disable: function(){
14531                 if(this.enabled){
14532                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14533                     this.enabled = false;
14534                 }
14535         }
14536 };/*
14537  * Based on:
14538  * Ext JS Library 1.1.1
14539  * Copyright(c) 2006-2007, Ext JS, LLC.
14540  *
14541  * Originally Released Under LGPL - original licence link has changed is not relivant.
14542  *
14543  * Fork - LGPL
14544  * <script type="text/javascript">
14545  */
14546
14547  
14548 /**
14549  * @class Roo.util.TextMetrics
14550  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14551  * wide, in pixels, a given block of text will be.
14552  * @singleton
14553  */
14554 Roo.util.TextMetrics = function(){
14555     var shared;
14556     return {
14557         /**
14558          * Measures the size of the specified text
14559          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14560          * that can affect the size of the rendered text
14561          * @param {String} text The text to measure
14562          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14563          * in order to accurately measure the text height
14564          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14565          */
14566         measure : function(el, text, fixedWidth){
14567             if(!shared){
14568                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14569             }
14570             shared.bind(el);
14571             shared.setFixedWidth(fixedWidth || 'auto');
14572             return shared.getSize(text);
14573         },
14574
14575         /**
14576          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14577          * the overhead of multiple calls to initialize the style properties on each measurement.
14578          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14579          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14580          * in order to accurately measure the text height
14581          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14582          */
14583         createInstance : function(el, fixedWidth){
14584             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14585         }
14586     };
14587 }();
14588
14589  
14590
14591 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14592     var ml = new Roo.Element(document.createElement('div'));
14593     document.body.appendChild(ml.dom);
14594     ml.position('absolute');
14595     ml.setLeftTop(-1000, -1000);
14596     ml.hide();
14597
14598     if(fixedWidth){
14599         ml.setWidth(fixedWidth);
14600     }
14601      
14602     var instance = {
14603         /**
14604          * Returns the size of the specified text based on the internal element's style and width properties
14605          * @memberOf Roo.util.TextMetrics.Instance#
14606          * @param {String} text The text to measure
14607          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14608          */
14609         getSize : function(text){
14610             ml.update(text);
14611             var s = ml.getSize();
14612             ml.update('');
14613             return s;
14614         },
14615
14616         /**
14617          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14618          * that can affect the size of the rendered text
14619          * @memberOf Roo.util.TextMetrics.Instance#
14620          * @param {String/HTMLElement} el The element, dom node or id
14621          */
14622         bind : function(el){
14623             ml.setStyle(
14624                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14625             );
14626         },
14627
14628         /**
14629          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14630          * to set a fixed width in order to accurately measure the text height.
14631          * @memberOf Roo.util.TextMetrics.Instance#
14632          * @param {Number} width The width to set on the element
14633          */
14634         setFixedWidth : function(width){
14635             ml.setWidth(width);
14636         },
14637
14638         /**
14639          * Returns the measured width of the specified text
14640          * @memberOf Roo.util.TextMetrics.Instance#
14641          * @param {String} text The text to measure
14642          * @return {Number} width The width in pixels
14643          */
14644         getWidth : function(text){
14645             ml.dom.style.width = 'auto';
14646             return this.getSize(text).width;
14647         },
14648
14649         /**
14650          * Returns the measured height of the specified text.  For multiline text, be sure to call
14651          * {@link #setFixedWidth} if necessary.
14652          * @memberOf Roo.util.TextMetrics.Instance#
14653          * @param {String} text The text to measure
14654          * @return {Number} height The height in pixels
14655          */
14656         getHeight : function(text){
14657             return this.getSize(text).height;
14658         }
14659     };
14660
14661     instance.bind(bindTo);
14662
14663     return instance;
14664 };
14665
14666 // backwards compat
14667 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14668  * Based on:
14669  * Ext JS Library 1.1.1
14670  * Copyright(c) 2006-2007, Ext JS, LLC.
14671  *
14672  * Originally Released Under LGPL - original licence link has changed is not relivant.
14673  *
14674  * Fork - LGPL
14675  * <script type="text/javascript">
14676  */
14677
14678 /**
14679  * @class Roo.state.Provider
14680  * Abstract base class for state provider implementations. This class provides methods
14681  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14682  * Provider interface.
14683  */
14684 Roo.state.Provider = function(){
14685     /**
14686      * @event statechange
14687      * Fires when a state change occurs.
14688      * @param {Provider} this This state provider
14689      * @param {String} key The state key which was changed
14690      * @param {String} value The encoded value for the state
14691      */
14692     this.addEvents({
14693         "statechange": true
14694     });
14695     this.state = {};
14696     Roo.state.Provider.superclass.constructor.call(this);
14697 };
14698 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14699     /**
14700      * Returns the current value for a key
14701      * @param {String} name The key name
14702      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14703      * @return {Mixed} The state data
14704      */
14705     get : function(name, defaultValue){
14706         return typeof this.state[name] == "undefined" ?
14707             defaultValue : this.state[name];
14708     },
14709     
14710     /**
14711      * Clears a value from the state
14712      * @param {String} name The key name
14713      */
14714     clear : function(name){
14715         delete this.state[name];
14716         this.fireEvent("statechange", this, name, null);
14717     },
14718     
14719     /**
14720      * Sets the value for a key
14721      * @param {String} name The key name
14722      * @param {Mixed} value The value to set
14723      */
14724     set : function(name, value){
14725         this.state[name] = value;
14726         this.fireEvent("statechange", this, name, value);
14727     },
14728     
14729     /**
14730      * Decodes a string previously encoded with {@link #encodeValue}.
14731      * @param {String} value The value to decode
14732      * @return {Mixed} The decoded value
14733      */
14734     decodeValue : function(cookie){
14735         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14736         var matches = re.exec(unescape(cookie));
14737         if(!matches || !matches[1]) return; // non state cookie
14738         var type = matches[1];
14739         var v = matches[2];
14740         switch(type){
14741             case "n":
14742                 return parseFloat(v);
14743             case "d":
14744                 return new Date(Date.parse(v));
14745             case "b":
14746                 return (v == "1");
14747             case "a":
14748                 var all = [];
14749                 var values = v.split("^");
14750                 for(var i = 0, len = values.length; i < len; i++){
14751                     all.push(this.decodeValue(values[i]));
14752                 }
14753                 return all;
14754            case "o":
14755                 var all = {};
14756                 var values = v.split("^");
14757                 for(var i = 0, len = values.length; i < len; i++){
14758                     var kv = values[i].split("=");
14759                     all[kv[0]] = this.decodeValue(kv[1]);
14760                 }
14761                 return all;
14762            default:
14763                 return v;
14764         }
14765     },
14766     
14767     /**
14768      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14769      * @param {Mixed} value The value to encode
14770      * @return {String} The encoded value
14771      */
14772     encodeValue : function(v){
14773         var enc;
14774         if(typeof v == "number"){
14775             enc = "n:" + v;
14776         }else if(typeof v == "boolean"){
14777             enc = "b:" + (v ? "1" : "0");
14778         }else if(v instanceof Date){
14779             enc = "d:" + v.toGMTString();
14780         }else if(v instanceof Array){
14781             var flat = "";
14782             for(var i = 0, len = v.length; i < len; i++){
14783                 flat += this.encodeValue(v[i]);
14784                 if(i != len-1) flat += "^";
14785             }
14786             enc = "a:" + flat;
14787         }else if(typeof v == "object"){
14788             var flat = "";
14789             for(var key in v){
14790                 if(typeof v[key] != "function"){
14791                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14792                 }
14793             }
14794             enc = "o:" + flat.substring(0, flat.length-1);
14795         }else{
14796             enc = "s:" + v;
14797         }
14798         return escape(enc);        
14799     }
14800 });
14801
14802 /*
14803  * Based on:
14804  * Ext JS Library 1.1.1
14805  * Copyright(c) 2006-2007, Ext JS, LLC.
14806  *
14807  * Originally Released Under LGPL - original licence link has changed is not relivant.
14808  *
14809  * Fork - LGPL
14810  * <script type="text/javascript">
14811  */
14812 /**
14813  * @class Roo.state.Manager
14814  * This is the global state manager. By default all components that are "state aware" check this class
14815  * for state information if you don't pass them a custom state provider. In order for this class
14816  * to be useful, it must be initialized with a provider when your application initializes.
14817  <pre><code>
14818 // in your initialization function
14819 init : function(){
14820    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14821    ...
14822    // supposed you have a {@link Roo.BorderLayout}
14823    var layout = new Roo.BorderLayout(...);
14824    layout.restoreState();
14825    // or a {Roo.BasicDialog}
14826    var dialog = new Roo.BasicDialog(...);
14827    dialog.restoreState();
14828  </code></pre>
14829  * @singleton
14830  */
14831 Roo.state.Manager = function(){
14832     var provider = new Roo.state.Provider();
14833     
14834     return {
14835         /**
14836          * Configures the default state provider for your application
14837          * @param {Provider} stateProvider The state provider to set
14838          */
14839         setProvider : function(stateProvider){
14840             provider = stateProvider;
14841         },
14842         
14843         /**
14844          * Returns the current value for a key
14845          * @param {String} name The key name
14846          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14847          * @return {Mixed} The state data
14848          */
14849         get : function(key, defaultValue){
14850             return provider.get(key, defaultValue);
14851         },
14852         
14853         /**
14854          * Sets the value for a key
14855          * @param {String} name The key name
14856          * @param {Mixed} value The state data
14857          */
14858          set : function(key, value){
14859             provider.set(key, value);
14860         },
14861         
14862         /**
14863          * Clears a value from the state
14864          * @param {String} name The key name
14865          */
14866         clear : function(key){
14867             provider.clear(key);
14868         },
14869         
14870         /**
14871          * Gets the currently configured state provider
14872          * @return {Provider} The state provider
14873          */
14874         getProvider : function(){
14875             return provider;
14876         }
14877     };
14878 }();
14879 /*
14880  * Based on:
14881  * Ext JS Library 1.1.1
14882  * Copyright(c) 2006-2007, Ext JS, LLC.
14883  *
14884  * Originally Released Under LGPL - original licence link has changed is not relivant.
14885  *
14886  * Fork - LGPL
14887  * <script type="text/javascript">
14888  */
14889 /**
14890  * @class Roo.state.CookieProvider
14891  * @extends Roo.state.Provider
14892  * The default Provider implementation which saves state via cookies.
14893  * <br />Usage:
14894  <pre><code>
14895    var cp = new Roo.state.CookieProvider({
14896        path: "/cgi-bin/",
14897        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14898        domain: "roojs.com"
14899    })
14900    Roo.state.Manager.setProvider(cp);
14901  </code></pre>
14902  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14903  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14904  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14905  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14906  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14907  * domain the page is running on including the 'www' like 'www.roojs.com')
14908  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14909  * @constructor
14910  * Create a new CookieProvider
14911  * @param {Object} config The configuration object
14912  */
14913 Roo.state.CookieProvider = function(config){
14914     Roo.state.CookieProvider.superclass.constructor.call(this);
14915     this.path = "/";
14916     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14917     this.domain = null;
14918     this.secure = false;
14919     Roo.apply(this, config);
14920     this.state = this.readCookies();
14921 };
14922
14923 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14924     // private
14925     set : function(name, value){
14926         if(typeof value == "undefined" || value === null){
14927             this.clear(name);
14928             return;
14929         }
14930         this.setCookie(name, value);
14931         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14932     },
14933
14934     // private
14935     clear : function(name){
14936         this.clearCookie(name);
14937         Roo.state.CookieProvider.superclass.clear.call(this, name);
14938     },
14939
14940     // private
14941     readCookies : function(){
14942         var cookies = {};
14943         var c = document.cookie + ";";
14944         var re = /\s?(.*?)=(.*?);/g;
14945         var matches;
14946         while((matches = re.exec(c)) != null){
14947             var name = matches[1];
14948             var value = matches[2];
14949             if(name && name.substring(0,3) == "ys-"){
14950                 cookies[name.substr(3)] = this.decodeValue(value);
14951             }
14952         }
14953         return cookies;
14954     },
14955
14956     // private
14957     setCookie : function(name, value){
14958         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14959            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14960            ((this.path == null) ? "" : ("; path=" + this.path)) +
14961            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14962            ((this.secure == true) ? "; secure" : "");
14963     },
14964
14965     // private
14966     clearCookie : function(name){
14967         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14968            ((this.path == null) ? "" : ("; path=" + this.path)) +
14969            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14970            ((this.secure == true) ? "; secure" : "");
14971     }
14972 });/*
14973  * Based on:
14974  * Ext JS Library 1.1.1
14975  * Copyright(c) 2006-2007, Ext JS, LLC.
14976  *
14977  * Originally Released Under LGPL - original licence link has changed is not relivant.
14978  *
14979  * Fork - LGPL
14980  * <script type="text/javascript">
14981  */
14982  
14983
14984 /**
14985  * @class Roo.ComponentMgr
14986  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14987  * @singleton
14988  */
14989 Roo.ComponentMgr = function(){
14990     var all = new Roo.util.MixedCollection();
14991
14992     return {
14993         /**
14994          * Registers a component.
14995          * @param {Roo.Component} c The component
14996          */
14997         register : function(c){
14998             all.add(c);
14999         },
15000
15001         /**
15002          * Unregisters a component.
15003          * @param {Roo.Component} c The component
15004          */
15005         unregister : function(c){
15006             all.remove(c);
15007         },
15008
15009         /**
15010          * Returns a component by id
15011          * @param {String} id The component id
15012          */
15013         get : function(id){
15014             return all.get(id);
15015         },
15016
15017         /**
15018          * Registers a function that will be called when a specified component is added to ComponentMgr
15019          * @param {String} id The component id
15020          * @param {Funtction} fn The callback function
15021          * @param {Object} scope The scope of the callback
15022          */
15023         onAvailable : function(id, fn, scope){
15024             all.on("add", function(index, o){
15025                 if(o.id == id){
15026                     fn.call(scope || o, o);
15027                     all.un("add", fn, scope);
15028                 }
15029             });
15030         }
15031     };
15032 }();/*
15033  * Based on:
15034  * Ext JS Library 1.1.1
15035  * Copyright(c) 2006-2007, Ext JS, LLC.
15036  *
15037  * Originally Released Under LGPL - original licence link has changed is not relivant.
15038  *
15039  * Fork - LGPL
15040  * <script type="text/javascript">
15041  */
15042  
15043 /**
15044  * @class Roo.Component
15045  * @extends Roo.util.Observable
15046  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15047  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15048  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15049  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15050  * All visual components (widgets) that require rendering into a layout should subclass Component.
15051  * @constructor
15052  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15053  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15054  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15055  */
15056 Roo.Component = function(config){
15057     config = config || {};
15058     if(config.tagName || config.dom || typeof config == "string"){ // element object
15059         config = {el: config, id: config.id || config};
15060     }
15061     this.initialConfig = config;
15062
15063     Roo.apply(this, config);
15064     this.addEvents({
15065         /**
15066          * @event disable
15067          * Fires after the component is disabled.
15068              * @param {Roo.Component} this
15069              */
15070         disable : true,
15071         /**
15072          * @event enable
15073          * Fires after the component is enabled.
15074              * @param {Roo.Component} this
15075              */
15076         enable : true,
15077         /**
15078          * @event beforeshow
15079          * Fires before the component is shown.  Return false to stop the show.
15080              * @param {Roo.Component} this
15081              */
15082         beforeshow : true,
15083         /**
15084          * @event show
15085          * Fires after the component is shown.
15086              * @param {Roo.Component} this
15087              */
15088         show : true,
15089         /**
15090          * @event beforehide
15091          * Fires before the component is hidden. Return false to stop the hide.
15092              * @param {Roo.Component} this
15093              */
15094         beforehide : true,
15095         /**
15096          * @event hide
15097          * Fires after the component is hidden.
15098              * @param {Roo.Component} this
15099              */
15100         hide : true,
15101         /**
15102          * @event beforerender
15103          * Fires before the component is rendered. Return false to stop the render.
15104              * @param {Roo.Component} this
15105              */
15106         beforerender : true,
15107         /**
15108          * @event render
15109          * Fires after the component is rendered.
15110              * @param {Roo.Component} this
15111              */
15112         render : true,
15113         /**
15114          * @event beforedestroy
15115          * Fires before the component is destroyed. Return false to stop the destroy.
15116              * @param {Roo.Component} this
15117              */
15118         beforedestroy : true,
15119         /**
15120          * @event destroy
15121          * Fires after the component is destroyed.
15122              * @param {Roo.Component} this
15123              */
15124         destroy : true
15125     });
15126     if(!this.id){
15127         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15128     }
15129     Roo.ComponentMgr.register(this);
15130     Roo.Component.superclass.constructor.call(this);
15131     this.initComponent();
15132     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15133         this.render(this.renderTo);
15134         delete this.renderTo;
15135     }
15136 };
15137
15138 /** @private */
15139 Roo.Component.AUTO_ID = 1000;
15140
15141 Roo.extend(Roo.Component, Roo.util.Observable, {
15142     /**
15143      * @scope Roo.Component.prototype
15144      * @type {Boolean}
15145      * true if this component is hidden. Read-only.
15146      */
15147     hidden : false,
15148     /**
15149      * @type {Boolean}
15150      * true if this component is disabled. Read-only.
15151      */
15152     disabled : false,
15153     /**
15154      * @type {Boolean}
15155      * true if this component has been rendered. Read-only.
15156      */
15157     rendered : false,
15158     
15159     /** @cfg {String} disableClass
15160      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15161      */
15162     disabledClass : "x-item-disabled",
15163         /** @cfg {Boolean} allowDomMove
15164          * Whether the component can move the Dom node when rendering (defaults to true).
15165          */
15166     allowDomMove : true,
15167     /** @cfg {String} hideMode
15168      * How this component should hidden. Supported values are
15169      * "visibility" (css visibility), "offsets" (negative offset position) and
15170      * "display" (css display) - defaults to "display".
15171      */
15172     hideMode: 'display',
15173
15174     /** @private */
15175     ctype : "Roo.Component",
15176
15177     /**
15178      * @cfg {String} actionMode 
15179      * which property holds the element that used for  hide() / show() / disable() / enable()
15180      * default is 'el' 
15181      */
15182     actionMode : "el",
15183
15184     /** @private */
15185     getActionEl : function(){
15186         return this[this.actionMode];
15187     },
15188
15189     initComponent : Roo.emptyFn,
15190     /**
15191      * If this is a lazy rendering component, render it to its container element.
15192      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15193      */
15194     render : function(container, position){
15195         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15196             if(!container && this.el){
15197                 this.el = Roo.get(this.el);
15198                 container = this.el.dom.parentNode;
15199                 this.allowDomMove = false;
15200             }
15201             this.container = Roo.get(container);
15202             this.rendered = true;
15203             if(position !== undefined){
15204                 if(typeof position == 'number'){
15205                     position = this.container.dom.childNodes[position];
15206                 }else{
15207                     position = Roo.getDom(position);
15208                 }
15209             }
15210             this.onRender(this.container, position || null);
15211             if(this.cls){
15212                 this.el.addClass(this.cls);
15213                 delete this.cls;
15214             }
15215             if(this.style){
15216                 this.el.applyStyles(this.style);
15217                 delete this.style;
15218             }
15219             this.fireEvent("render", this);
15220             this.afterRender(this.container);
15221             if(this.hidden){
15222                 this.hide();
15223             }
15224             if(this.disabled){
15225                 this.disable();
15226             }
15227         }
15228         return this;
15229     },
15230
15231     /** @private */
15232     // default function is not really useful
15233     onRender : function(ct, position){
15234         if(this.el){
15235             this.el = Roo.get(this.el);
15236             if(this.allowDomMove !== false){
15237                 ct.dom.insertBefore(this.el.dom, position);
15238             }
15239         }
15240     },
15241
15242     /** @private */
15243     getAutoCreate : function(){
15244         var cfg = typeof this.autoCreate == "object" ?
15245                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15246         if(this.id && !cfg.id){
15247             cfg.id = this.id;
15248         }
15249         return cfg;
15250     },
15251
15252     /** @private */
15253     afterRender : Roo.emptyFn,
15254
15255     /**
15256      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15257      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15258      */
15259     destroy : function(){
15260         if(this.fireEvent("beforedestroy", this) !== false){
15261             this.purgeListeners();
15262             this.beforeDestroy();
15263             if(this.rendered){
15264                 this.el.removeAllListeners();
15265                 this.el.remove();
15266                 if(this.actionMode == "container"){
15267                     this.container.remove();
15268                 }
15269             }
15270             this.onDestroy();
15271             Roo.ComponentMgr.unregister(this);
15272             this.fireEvent("destroy", this);
15273         }
15274     },
15275
15276         /** @private */
15277     beforeDestroy : function(){
15278
15279     },
15280
15281         /** @private */
15282         onDestroy : function(){
15283
15284     },
15285
15286     /**
15287      * Returns the underlying {@link Roo.Element}.
15288      * @return {Roo.Element} The element
15289      */
15290     getEl : function(){
15291         return this.el;
15292     },
15293
15294     /**
15295      * Returns the id of this component.
15296      * @return {String}
15297      */
15298     getId : function(){
15299         return this.id;
15300     },
15301
15302     /**
15303      * Try to focus this component.
15304      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15305      * @return {Roo.Component} this
15306      */
15307     focus : function(selectText){
15308         if(this.rendered){
15309             this.el.focus();
15310             if(selectText === true){
15311                 this.el.dom.select();
15312             }
15313         }
15314         return this;
15315     },
15316
15317     /** @private */
15318     blur : function(){
15319         if(this.rendered){
15320             this.el.blur();
15321         }
15322         return this;
15323     },
15324
15325     /**
15326      * Disable this component.
15327      * @return {Roo.Component} this
15328      */
15329     disable : function(){
15330         if(this.rendered){
15331             this.onDisable();
15332         }
15333         this.disabled = true;
15334         this.fireEvent("disable", this);
15335         return this;
15336     },
15337
15338         // private
15339     onDisable : function(){
15340         this.getActionEl().addClass(this.disabledClass);
15341         this.el.dom.disabled = true;
15342     },
15343
15344     /**
15345      * Enable this component.
15346      * @return {Roo.Component} this
15347      */
15348     enable : function(){
15349         if(this.rendered){
15350             this.onEnable();
15351         }
15352         this.disabled = false;
15353         this.fireEvent("enable", this);
15354         return this;
15355     },
15356
15357         // private
15358     onEnable : function(){
15359         this.getActionEl().removeClass(this.disabledClass);
15360         this.el.dom.disabled = false;
15361     },
15362
15363     /**
15364      * Convenience function for setting disabled/enabled by boolean.
15365      * @param {Boolean} disabled
15366      */
15367     setDisabled : function(disabled){
15368         this[disabled ? "disable" : "enable"]();
15369     },
15370
15371     /**
15372      * Show this component.
15373      * @return {Roo.Component} this
15374      */
15375     show: function(){
15376         if(this.fireEvent("beforeshow", this) !== false){
15377             this.hidden = false;
15378             if(this.rendered){
15379                 this.onShow();
15380             }
15381             this.fireEvent("show", this);
15382         }
15383         return this;
15384     },
15385
15386     // private
15387     onShow : function(){
15388         var ae = this.getActionEl();
15389         if(this.hideMode == 'visibility'){
15390             ae.dom.style.visibility = "visible";
15391         }else if(this.hideMode == 'offsets'){
15392             ae.removeClass('x-hidden');
15393         }else{
15394             ae.dom.style.display = "";
15395         }
15396     },
15397
15398     /**
15399      * Hide this component.
15400      * @return {Roo.Component} this
15401      */
15402     hide: function(){
15403         if(this.fireEvent("beforehide", this) !== false){
15404             this.hidden = true;
15405             if(this.rendered){
15406                 this.onHide();
15407             }
15408             this.fireEvent("hide", this);
15409         }
15410         return this;
15411     },
15412
15413     // private
15414     onHide : function(){
15415         var ae = this.getActionEl();
15416         if(this.hideMode == 'visibility'){
15417             ae.dom.style.visibility = "hidden";
15418         }else if(this.hideMode == 'offsets'){
15419             ae.addClass('x-hidden');
15420         }else{
15421             ae.dom.style.display = "none";
15422         }
15423     },
15424
15425     /**
15426      * Convenience function to hide or show this component by boolean.
15427      * @param {Boolean} visible True to show, false to hide
15428      * @return {Roo.Component} this
15429      */
15430     setVisible: function(visible){
15431         if(visible) {
15432             this.show();
15433         }else{
15434             this.hide();
15435         }
15436         return this;
15437     },
15438
15439     /**
15440      * Returns true if this component is visible.
15441      */
15442     isVisible : function(){
15443         return this.getActionEl().isVisible();
15444     },
15445
15446     cloneConfig : function(overrides){
15447         overrides = overrides || {};
15448         var id = overrides.id || Roo.id();
15449         var cfg = Roo.applyIf(overrides, this.initialConfig);
15450         cfg.id = id; // prevent dup id
15451         return new this.constructor(cfg);
15452     }
15453 });/*
15454  * Based on:
15455  * Ext JS Library 1.1.1
15456  * Copyright(c) 2006-2007, Ext JS, LLC.
15457  *
15458  * Originally Released Under LGPL - original licence link has changed is not relivant.
15459  *
15460  * Fork - LGPL
15461  * <script type="text/javascript">
15462  */
15463
15464 /**
15465  * @class Roo.BoxComponent
15466  * @extends Roo.Component
15467  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15468  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15469  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15470  * layout containers.
15471  * @constructor
15472  * @param {Roo.Element/String/Object} config The configuration options.
15473  */
15474 Roo.BoxComponent = function(config){
15475     Roo.Component.call(this, config);
15476     this.addEvents({
15477         /**
15478          * @event resize
15479          * Fires after the component is resized.
15480              * @param {Roo.Component} this
15481              * @param {Number} adjWidth The box-adjusted width that was set
15482              * @param {Number} adjHeight The box-adjusted height that was set
15483              * @param {Number} rawWidth The width that was originally specified
15484              * @param {Number} rawHeight The height that was originally specified
15485              */
15486         resize : true,
15487         /**
15488          * @event move
15489          * Fires after the component is moved.
15490              * @param {Roo.Component} this
15491              * @param {Number} x The new x position
15492              * @param {Number} y The new y position
15493              */
15494         move : true
15495     });
15496 };
15497
15498 Roo.extend(Roo.BoxComponent, Roo.Component, {
15499     // private, set in afterRender to signify that the component has been rendered
15500     boxReady : false,
15501     // private, used to defer height settings to subclasses
15502     deferHeight: false,
15503     /** @cfg {Number} width
15504      * width (optional) size of component
15505      */
15506      /** @cfg {Number} height
15507      * height (optional) size of component
15508      */
15509      
15510     /**
15511      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15512      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15513      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15514      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15515      * @return {Roo.BoxComponent} this
15516      */
15517     setSize : function(w, h){
15518         // support for standard size objects
15519         if(typeof w == 'object'){
15520             h = w.height;
15521             w = w.width;
15522         }
15523         // not rendered
15524         if(!this.boxReady){
15525             this.width = w;
15526             this.height = h;
15527             return this;
15528         }
15529
15530         // prevent recalcs when not needed
15531         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15532             return this;
15533         }
15534         this.lastSize = {width: w, height: h};
15535
15536         var adj = this.adjustSize(w, h);
15537         var aw = adj.width, ah = adj.height;
15538         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15539             var rz = this.getResizeEl();
15540             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15541                 rz.setSize(aw, ah);
15542             }else if(!this.deferHeight && ah !== undefined){
15543                 rz.setHeight(ah);
15544             }else if(aw !== undefined){
15545                 rz.setWidth(aw);
15546             }
15547             this.onResize(aw, ah, w, h);
15548             this.fireEvent('resize', this, aw, ah, w, h);
15549         }
15550         return this;
15551     },
15552
15553     /**
15554      * Gets the current size of the component's underlying element.
15555      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15556      */
15557     getSize : function(){
15558         return this.el.getSize();
15559     },
15560
15561     /**
15562      * Gets the current XY position of the component's underlying element.
15563      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15564      * @return {Array} The XY position of the element (e.g., [100, 200])
15565      */
15566     getPosition : function(local){
15567         if(local === true){
15568             return [this.el.getLeft(true), this.el.getTop(true)];
15569         }
15570         return this.xy || this.el.getXY();
15571     },
15572
15573     /**
15574      * Gets the current box measurements of the component's underlying element.
15575      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15576      * @returns {Object} box An object in the format {x, y, width, height}
15577      */
15578     getBox : function(local){
15579         var s = this.el.getSize();
15580         if(local){
15581             s.x = this.el.getLeft(true);
15582             s.y = this.el.getTop(true);
15583         }else{
15584             var xy = this.xy || this.el.getXY();
15585             s.x = xy[0];
15586             s.y = xy[1];
15587         }
15588         return s;
15589     },
15590
15591     /**
15592      * Sets the current box measurements of the component's underlying element.
15593      * @param {Object} box An object in the format {x, y, width, height}
15594      * @returns {Roo.BoxComponent} this
15595      */
15596     updateBox : function(box){
15597         this.setSize(box.width, box.height);
15598         this.setPagePosition(box.x, box.y);
15599         return this;
15600     },
15601
15602     // protected
15603     getResizeEl : function(){
15604         return this.resizeEl || this.el;
15605     },
15606
15607     // protected
15608     getPositionEl : function(){
15609         return this.positionEl || this.el;
15610     },
15611
15612     /**
15613      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15614      * This method fires the move event.
15615      * @param {Number} left The new left
15616      * @param {Number} top The new top
15617      * @returns {Roo.BoxComponent} this
15618      */
15619     setPosition : function(x, y){
15620         this.x = x;
15621         this.y = y;
15622         if(!this.boxReady){
15623             return this;
15624         }
15625         var adj = this.adjustPosition(x, y);
15626         var ax = adj.x, ay = adj.y;
15627
15628         var el = this.getPositionEl();
15629         if(ax !== undefined || ay !== undefined){
15630             if(ax !== undefined && ay !== undefined){
15631                 el.setLeftTop(ax, ay);
15632             }else if(ax !== undefined){
15633                 el.setLeft(ax);
15634             }else if(ay !== undefined){
15635                 el.setTop(ay);
15636             }
15637             this.onPosition(ax, ay);
15638             this.fireEvent('move', this, ax, ay);
15639         }
15640         return this;
15641     },
15642
15643     /**
15644      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15645      * This method fires the move event.
15646      * @param {Number} x The new x position
15647      * @param {Number} y The new y position
15648      * @returns {Roo.BoxComponent} this
15649      */
15650     setPagePosition : function(x, y){
15651         this.pageX = x;
15652         this.pageY = y;
15653         if(!this.boxReady){
15654             return;
15655         }
15656         if(x === undefined || y === undefined){ // cannot translate undefined points
15657             return;
15658         }
15659         var p = this.el.translatePoints(x, y);
15660         this.setPosition(p.left, p.top);
15661         return this;
15662     },
15663
15664     // private
15665     onRender : function(ct, position){
15666         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15667         if(this.resizeEl){
15668             this.resizeEl = Roo.get(this.resizeEl);
15669         }
15670         if(this.positionEl){
15671             this.positionEl = Roo.get(this.positionEl);
15672         }
15673     },
15674
15675     // private
15676     afterRender : function(){
15677         Roo.BoxComponent.superclass.afterRender.call(this);
15678         this.boxReady = true;
15679         this.setSize(this.width, this.height);
15680         if(this.x || this.y){
15681             this.setPosition(this.x, this.y);
15682         }
15683         if(this.pageX || this.pageY){
15684             this.setPagePosition(this.pageX, this.pageY);
15685         }
15686     },
15687
15688     /**
15689      * Force the component's size to recalculate based on the underlying element's current height and width.
15690      * @returns {Roo.BoxComponent} this
15691      */
15692     syncSize : function(){
15693         delete this.lastSize;
15694         this.setSize(this.el.getWidth(), this.el.getHeight());
15695         return this;
15696     },
15697
15698     /**
15699      * Called after the component is resized, this method is empty by default but can be implemented by any
15700      * subclass that needs to perform custom logic after a resize occurs.
15701      * @param {Number} adjWidth The box-adjusted width that was set
15702      * @param {Number} adjHeight The box-adjusted height that was set
15703      * @param {Number} rawWidth The width that was originally specified
15704      * @param {Number} rawHeight The height that was originally specified
15705      */
15706     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15707
15708     },
15709
15710     /**
15711      * Called after the component is moved, this method is empty by default but can be implemented by any
15712      * subclass that needs to perform custom logic after a move occurs.
15713      * @param {Number} x The new x position
15714      * @param {Number} y The new y position
15715      */
15716     onPosition : function(x, y){
15717
15718     },
15719
15720     // private
15721     adjustSize : function(w, h){
15722         if(this.autoWidth){
15723             w = 'auto';
15724         }
15725         if(this.autoHeight){
15726             h = 'auto';
15727         }
15728         return {width : w, height: h};
15729     },
15730
15731     // private
15732     adjustPosition : function(x, y){
15733         return {x : x, y: y};
15734     }
15735 });/*
15736  * Original code for Roojs - LGPL
15737  * <script type="text/javascript">
15738  */
15739  
15740 /**
15741  * @class Roo.XComponent
15742  * A delayed Element creator...
15743  * Or a way to group chunks of interface together.
15744  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15745  *  used in conjunction with XComponent.build() it will create an instance of each element,
15746  *  then call addxtype() to build the User interface.
15747  * 
15748  * Mypart.xyx = new Roo.XComponent({
15749
15750     parent : 'Mypart.xyz', // empty == document.element.!!
15751     order : '001',
15752     name : 'xxxx'
15753     region : 'xxxx'
15754     disabled : function() {} 
15755      
15756     tree : function() { // return an tree of xtype declared components
15757         var MODULE = this;
15758         return 
15759         {
15760             xtype : 'NestedLayoutPanel',
15761             // technicall
15762         }
15763      ]
15764  *})
15765  *
15766  *
15767  * It can be used to build a big heiracy, with parent etc.
15768  * or you can just use this to render a single compoent to a dom element
15769  * MYPART.render(Roo.Element | String(id) | dom_element )
15770  *
15771  *
15772  * Usage patterns.
15773  *
15774  * Classic Roo
15775  *
15776  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15777  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15778  *
15779  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15780  *
15781  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15782  * - if mulitple topModules exist, the last one is defined as the top module.
15783  *
15784  * Embeded Roo
15785  * 
15786  * When the top level or multiple modules are to embedded into a existing HTML page,
15787  * the parent element can container '#id' of the element where the module will be drawn.
15788  *
15789  * Bootstrap Roo
15790  *
15791  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15792  * it relies more on a include mechanism, where sub modules are included into an outer page.
15793  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15794  * 
15795  * Bootstrap Roo Included elements
15796  *
15797  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15798  * hence confusing the component builder as it thinks there are multiple top level elements. 
15799  *
15800  * 
15801  * 
15802  * @extends Roo.util.Observable
15803  * @constructor
15804  * @param cfg {Object} configuration of component
15805  * 
15806  */
15807 Roo.XComponent = function(cfg) {
15808     Roo.apply(this, cfg);
15809     this.addEvents({ 
15810         /**
15811              * @event built
15812              * Fires when this the componnt is built
15813              * @param {Roo.XComponent} c the component
15814              */
15815         'built' : true
15816         
15817     });
15818     this.region = this.region || 'center'; // default..
15819     Roo.XComponent.register(this);
15820     this.modules = false;
15821     this.el = false; // where the layout goes..
15822     
15823     
15824 }
15825 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15826     /**
15827      * @property el
15828      * The created element (with Roo.factory())
15829      * @type {Roo.Layout}
15830      */
15831     el  : false,
15832     
15833     /**
15834      * @property el
15835      * for BC  - use el in new code
15836      * @type {Roo.Layout}
15837      */
15838     panel : false,
15839     
15840     /**
15841      * @property layout
15842      * for BC  - use el in new code
15843      * @type {Roo.Layout}
15844      */
15845     layout : false,
15846     
15847      /**
15848      * @cfg {Function|boolean} disabled
15849      * If this module is disabled by some rule, return true from the funtion
15850      */
15851     disabled : false,
15852     
15853     /**
15854      * @cfg {String} parent 
15855      * Name of parent element which it get xtype added to..
15856      */
15857     parent: false,
15858     
15859     /**
15860      * @cfg {String} order
15861      * Used to set the order in which elements are created (usefull for multiple tabs)
15862      */
15863     
15864     order : false,
15865     /**
15866      * @cfg {String} name
15867      * String to display while loading.
15868      */
15869     name : false,
15870     /**
15871      * @cfg {String} region
15872      * Region to render component to (defaults to center)
15873      */
15874     region : 'center',
15875     
15876     /**
15877      * @cfg {Array} items
15878      * A single item array - the first element is the root of the tree..
15879      * It's done this way to stay compatible with the Xtype system...
15880      */
15881     items : false,
15882     
15883     /**
15884      * @property _tree
15885      * The method that retuns the tree of parts that make up this compoennt 
15886      * @type {function}
15887      */
15888     _tree  : false,
15889     
15890      /**
15891      * render
15892      * render element to dom or tree
15893      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15894      */
15895     
15896     render : function(el)
15897     {
15898         
15899         el = el || false;
15900         var hp = this.parent ? 1 : 0;
15901         Roo.log(this);
15902         
15903         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15904             // if parent is a '#.....' string, then let's use that..
15905             var ename = this.parent.substr(1);
15906             this.parent = false;
15907             Roo.log(ename);
15908             switch (ename) {
15909                 case 'bootstrap-body' :
15910                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15911                         this.parent = { el :  new  Roo.bootstrap.Body() };
15912                         Roo.log("setting el to doc body");
15913                          
15914                     } else {
15915                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15916                     }
15917                     break;
15918                 case 'bootstrap':
15919                     this.parent = { el : true};
15920                     // fall through
15921                 default:
15922                     el = Roo.get(ename);
15923                     break;
15924             }
15925                 
15926             
15927             if (!el && !this.parent) {
15928                 Roo.log("Warning - element can not be found :#" + ename );
15929                 return;
15930             }
15931         }
15932         Roo.log("EL:");Roo.log(el);
15933         Roo.log("this.parent.el:");Roo.log(this.parent.el);
15934         
15935         var tree = this._tree ? this._tree() : this.tree();
15936
15937         // altertive root elements ??? - we need a better way to indicate these.
15938         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15939                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15940         
15941         if (!this.parent && is_alt) {
15942             //el = Roo.get(document.body);
15943             this.parent = { el : true };
15944         }
15945             
15946             
15947         
15948         if (!this.parent) {
15949             
15950             Roo.log("no parent - creating one");
15951             
15952             el = el ? Roo.get(el) : false;      
15953             
15954             // it's a top level one..
15955             this.parent =  {
15956                 el : new Roo.BorderLayout(el || document.body, {
15957                 
15958                      center: {
15959                          titlebar: false,
15960                          autoScroll:false,
15961                          closeOnTab: true,
15962                          tabPosition: 'top',
15963                           //resizeTabs: true,
15964                          alwaysShowTabs: el && hp? false :  true,
15965                          hideTabs: el || !hp ? true :  false,
15966                          minTabWidth: 140
15967                      }
15968                  })
15969             }
15970         }
15971         
15972                 if (!this.parent.el) {
15973                         // probably an old style ctor, which has been disabled.
15974                         return;
15975                         
15976                 }
15977                 // The 'tree' method is  '_tree now' 
15978             
15979         tree.region = tree.region || this.region;
15980         
15981         if (this.parent.el === true) {
15982             // bootstrap... - body..
15983             this.parent.el = Roo.factory(tree);
15984         }
15985         
15986         this.el = this.parent.el.addxtype(tree);
15987         this.fireEvent('built', this);
15988         
15989         this.panel = this.el;
15990         this.layout = this.panel.layout;
15991                 this.parentLayout = this.parent.layout  || false;  
15992          
15993     }
15994     
15995 });
15996
15997 Roo.apply(Roo.XComponent, {
15998     /**
15999      * @property  hideProgress
16000      * true to disable the building progress bar.. usefull on single page renders.
16001      * @type Boolean
16002      */
16003     hideProgress : false,
16004     /**
16005      * @property  buildCompleted
16006      * True when the builder has completed building the interface.
16007      * @type Boolean
16008      */
16009     buildCompleted : false,
16010      
16011     /**
16012      * @property  topModule
16013      * the upper most module - uses document.element as it's constructor.
16014      * @type Object
16015      */
16016      
16017     topModule  : false,
16018       
16019     /**
16020      * @property  modules
16021      * array of modules to be created by registration system.
16022      * @type {Array} of Roo.XComponent
16023      */
16024     
16025     modules : [],
16026     /**
16027      * @property  elmodules
16028      * array of modules to be created by which use #ID 
16029      * @type {Array} of Roo.XComponent
16030      */
16031      
16032     elmodules : [],
16033
16034      /**
16035      * @property  build_from_html
16036      * Build elements from html - used by bootstrap HTML stuff 
16037      *    - this is cleared after build is completed
16038      * @type {boolean} true  (default false)
16039      */
16040      
16041     build_from_html : false,
16042
16043     /**
16044      * Register components to be built later.
16045      *
16046      * This solves the following issues
16047      * - Building is not done on page load, but after an authentication process has occured.
16048      * - Interface elements are registered on page load
16049      * - Parent Interface elements may not be loaded before child, so this handles that..
16050      * 
16051      *
16052      * example:
16053      * 
16054      * MyApp.register({
16055           order : '000001',
16056           module : 'Pman.Tab.projectMgr',
16057           region : 'center',
16058           parent : 'Pman.layout',
16059           disabled : false,  // or use a function..
16060         })
16061      
16062      * * @param {Object} details about module
16063      */
16064     register : function(obj) {
16065                 
16066         Roo.XComponent.event.fireEvent('register', obj);
16067         switch(typeof(obj.disabled) ) {
16068                 
16069             case 'undefined':
16070                 break;
16071             
16072             case 'function':
16073                 if ( obj.disabled() ) {
16074                         return;
16075                 }
16076                 break;
16077             
16078             default:
16079                 if (obj.disabled) {
16080                         return;
16081                 }
16082                 break;
16083         }
16084                 
16085         this.modules.push(obj);
16086          
16087     },
16088     /**
16089      * convert a string to an object..
16090      * eg. 'AAA.BBB' -> finds AAA.BBB
16091
16092      */
16093     
16094     toObject : function(str)
16095     {
16096         if (!str || typeof(str) == 'object') {
16097             return str;
16098         }
16099         if (str.substring(0,1) == '#') {
16100             return str;
16101         }
16102
16103         var ar = str.split('.');
16104         var rt, o;
16105         rt = ar.shift();
16106             /** eval:var:o */
16107         try {
16108             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16109         } catch (e) {
16110             throw "Module not found : " + str;
16111         }
16112         
16113         if (o === false) {
16114             throw "Module not found : " + str;
16115         }
16116         Roo.each(ar, function(e) {
16117             if (typeof(o[e]) == 'undefined') {
16118                 throw "Module not found : " + str;
16119             }
16120             o = o[e];
16121         });
16122         
16123         return o;
16124         
16125     },
16126     
16127     
16128     /**
16129      * move modules into their correct place in the tree..
16130      * 
16131      */
16132     preBuild : function ()
16133     {
16134         var _t = this;
16135         Roo.each(this.modules , function (obj)
16136         {
16137             Roo.XComponent.event.fireEvent('beforebuild', obj);
16138             
16139             var opar = obj.parent;
16140             try { 
16141                 obj.parent = this.toObject(opar);
16142             } catch(e) {
16143                 Roo.log("parent:toObject failed: " + e.toString());
16144                 return;
16145             }
16146             
16147             if (!obj.parent) {
16148                 Roo.debug && Roo.log("GOT top level module");
16149                 Roo.debug && Roo.log(obj);
16150                 obj.modules = new Roo.util.MixedCollection(false, 
16151                     function(o) { return o.order + '' }
16152                 );
16153                 this.topModule = obj;
16154                 return;
16155             }
16156                         // parent is a string (usually a dom element name..)
16157             if (typeof(obj.parent) == 'string') {
16158                 this.elmodules.push(obj);
16159                 return;
16160             }
16161             if (obj.parent.constructor != Roo.XComponent) {
16162                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16163             }
16164             if (!obj.parent.modules) {
16165                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16166                     function(o) { return o.order + '' }
16167                 );
16168             }
16169             if (obj.parent.disabled) {
16170                 obj.disabled = true;
16171             }
16172             obj.parent.modules.add(obj);
16173         }, this);
16174     },
16175     
16176      /**
16177      * make a list of modules to build.
16178      * @return {Array} list of modules. 
16179      */ 
16180     
16181     buildOrder : function()
16182     {
16183         var _this = this;
16184         var cmp = function(a,b) {   
16185             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16186         };
16187         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16188             throw "No top level modules to build";
16189         }
16190         
16191         // make a flat list in order of modules to build.
16192         var mods = this.topModule ? [ this.topModule ] : [];
16193                 
16194         
16195         // elmodules (is a list of DOM based modules )
16196         Roo.each(this.elmodules, function(e) {
16197             mods.push(e);
16198             if (!this.topModule &&
16199                 typeof(e.parent) == 'string' &&
16200                 e.parent.substring(0,1) == '#' &&
16201                 Roo.get(e.parent.substr(1))
16202                ) {
16203                 
16204                 _this.topModule = e;
16205             }
16206             
16207         });
16208
16209         
16210         // add modules to their parents..
16211         var addMod = function(m) {
16212             Roo.debug && Roo.log("build Order: add: " + m.name);
16213                 
16214             mods.push(m);
16215             if (m.modules && !m.disabled) {
16216                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16217                 m.modules.keySort('ASC',  cmp );
16218                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16219     
16220                 m.modules.each(addMod);
16221             } else {
16222                 Roo.debug && Roo.log("build Order: no child modules");
16223             }
16224             // not sure if this is used any more..
16225             if (m.finalize) {
16226                 m.finalize.name = m.name + " (clean up) ";
16227                 mods.push(m.finalize);
16228             }
16229             
16230         }
16231         if (this.topModule && this.topModule.modules) { 
16232             this.topModule.modules.keySort('ASC',  cmp );
16233             this.topModule.modules.each(addMod);
16234         } 
16235         return mods;
16236     },
16237     
16238      /**
16239      * Build the registered modules.
16240      * @param {Object} parent element.
16241      * @param {Function} optional method to call after module has been added.
16242      * 
16243      */ 
16244    
16245     build : function(opts) 
16246     {
16247         
16248         if (typeof(opts) != 'undefined') {
16249             Roo.apply(this,opts);
16250         }
16251         
16252         this.preBuild();
16253         var mods = this.buildOrder();
16254       
16255         //this.allmods = mods;
16256         //Roo.debug && Roo.log(mods);
16257         //return;
16258         if (!mods.length) { // should not happen
16259             throw "NO modules!!!";
16260         }
16261         
16262         
16263         var msg = "Building Interface...";
16264         // flash it up as modal - so we store the mask!?
16265         if (!this.hideProgress && Roo.MessageBox) {
16266             Roo.MessageBox.show({ title: 'loading' });
16267             Roo.MessageBox.show({
16268                title: "Please wait...",
16269                msg: msg,
16270                width:450,
16271                progress:true,
16272                closable:false,
16273                modal: false
16274               
16275             });
16276         }
16277         var total = mods.length;
16278         
16279         var _this = this;
16280         var progressRun = function() {
16281             if (!mods.length) {
16282                 Roo.debug && Roo.log('hide?');
16283                 if (!this.hideProgress && Roo.MessageBox) {
16284                     Roo.MessageBox.hide();
16285                 }
16286                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16287                 
16288                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16289                 
16290                 // THE END...
16291                 return false;   
16292             }
16293             
16294             var m = mods.shift();
16295             
16296             
16297             Roo.debug && Roo.log(m);
16298             // not sure if this is supported any more.. - modules that are are just function
16299             if (typeof(m) == 'function') { 
16300                 m.call(this);
16301                 return progressRun.defer(10, _this);
16302             } 
16303             
16304             
16305             msg = "Building Interface " + (total  - mods.length) + 
16306                     " of " + total + 
16307                     (m.name ? (' - ' + m.name) : '');
16308                         Roo.debug && Roo.log(msg);
16309             if (!this.hideProgress &&  Roo.MessageBox) { 
16310                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16311             }
16312             
16313          
16314             // is the module disabled?
16315             var disabled = (typeof(m.disabled) == 'function') ?
16316                 m.disabled.call(m.module.disabled) : m.disabled;    
16317             
16318             
16319             if (disabled) {
16320                 return progressRun(); // we do not update the display!
16321             }
16322             
16323             // now build 
16324             
16325                         
16326                         
16327             m.render();
16328             // it's 10 on top level, and 1 on others??? why...
16329             return progressRun.defer(10, _this);
16330              
16331         }
16332         progressRun.defer(1, _this);
16333      
16334         
16335         
16336     },
16337         
16338         
16339         /**
16340          * Event Object.
16341          *
16342          *
16343          */
16344         event: false, 
16345     /**
16346          * wrapper for event.on - aliased later..  
16347          * Typically use to register a event handler for register:
16348          *
16349          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16350          *
16351          */
16352     on : false
16353    
16354     
16355     
16356 });
16357
16358 Roo.XComponent.event = new Roo.util.Observable({
16359                 events : { 
16360                         /**
16361                          * @event register
16362                          * Fires when an Component is registered,
16363                          * set the disable property on the Component to stop registration.
16364                          * @param {Roo.XComponent} c the component being registerd.
16365                          * 
16366                          */
16367                         'register' : true,
16368             /**
16369                          * @event beforebuild
16370                          * Fires before each Component is built
16371                          * can be used to apply permissions.
16372                          * @param {Roo.XComponent} c the component being registerd.
16373                          * 
16374                          */
16375                         'beforebuild' : true,
16376                         /**
16377                          * @event buildcomplete
16378                          * Fires on the top level element when all elements have been built
16379                          * @param {Roo.XComponent} the top level component.
16380                          */
16381                         'buildcomplete' : true
16382                         
16383                 }
16384 });
16385
16386 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16387  /*
16388  * Based on:
16389  * Ext JS Library 1.1.1
16390  * Copyright(c) 2006-2007, Ext JS, LLC.
16391  *
16392  * Originally Released Under LGPL - original licence link has changed is not relivant.
16393  *
16394  * Fork - LGPL
16395  * <script type="text/javascript">
16396  */
16397
16398
16399
16400 /*
16401  * These classes are derivatives of the similarly named classes in the YUI Library.
16402  * The original license:
16403  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16404  * Code licensed under the BSD License:
16405  * http://developer.yahoo.net/yui/license.txt
16406  */
16407
16408 (function() {
16409
16410 var Event=Roo.EventManager;
16411 var Dom=Roo.lib.Dom;
16412
16413 /**
16414  * @class Roo.dd.DragDrop
16415  * @extends Roo.util.Observable
16416  * Defines the interface and base operation of items that that can be
16417  * dragged or can be drop targets.  It was designed to be extended, overriding
16418  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16419  * Up to three html elements can be associated with a DragDrop instance:
16420  * <ul>
16421  * <li>linked element: the element that is passed into the constructor.
16422  * This is the element which defines the boundaries for interaction with
16423  * other DragDrop objects.</li>
16424  * <li>handle element(s): The drag operation only occurs if the element that
16425  * was clicked matches a handle element.  By default this is the linked
16426  * element, but there are times that you will want only a portion of the
16427  * linked element to initiate the drag operation, and the setHandleElId()
16428  * method provides a way to define this.</li>
16429  * <li>drag element: this represents the element that would be moved along
16430  * with the cursor during a drag operation.  By default, this is the linked
16431  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16432  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16433  * </li>
16434  * </ul>
16435  * This class should not be instantiated until the onload event to ensure that
16436  * the associated elements are available.
16437  * The following would define a DragDrop obj that would interact with any
16438  * other DragDrop obj in the "group1" group:
16439  * <pre>
16440  *  dd = new Roo.dd.DragDrop("div1", "group1");
16441  * </pre>
16442  * Since none of the event handlers have been implemented, nothing would
16443  * actually happen if you were to run the code above.  Normally you would
16444  * override this class or one of the default implementations, but you can
16445  * also override the methods you want on an instance of the class...
16446  * <pre>
16447  *  dd.onDragDrop = function(e, id) {
16448  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16449  *  }
16450  * </pre>
16451  * @constructor
16452  * @param {String} id of the element that is linked to this instance
16453  * @param {String} sGroup the group of related DragDrop objects
16454  * @param {object} config an object containing configurable attributes
16455  *                Valid properties for DragDrop:
16456  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16457  */
16458 Roo.dd.DragDrop = function(id, sGroup, config) {
16459     if (id) {
16460         this.init(id, sGroup, config);
16461     }
16462     
16463 };
16464
16465 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16466
16467     /**
16468      * The id of the element associated with this object.  This is what we
16469      * refer to as the "linked element" because the size and position of
16470      * this element is used to determine when the drag and drop objects have
16471      * interacted.
16472      * @property id
16473      * @type String
16474      */
16475     id: null,
16476
16477     /**
16478      * Configuration attributes passed into the constructor
16479      * @property config
16480      * @type object
16481      */
16482     config: null,
16483
16484     /**
16485      * The id of the element that will be dragged.  By default this is same
16486      * as the linked element , but could be changed to another element. Ex:
16487      * Roo.dd.DDProxy
16488      * @property dragElId
16489      * @type String
16490      * @private
16491      */
16492     dragElId: null,
16493
16494     /**
16495      * the id of the element that initiates the drag operation.  By default
16496      * this is the linked element, but could be changed to be a child of this
16497      * element.  This lets us do things like only starting the drag when the
16498      * header element within the linked html element is clicked.
16499      * @property handleElId
16500      * @type String
16501      * @private
16502      */
16503     handleElId: null,
16504
16505     /**
16506      * An associative array of HTML tags that will be ignored if clicked.
16507      * @property invalidHandleTypes
16508      * @type {string: string}
16509      */
16510     invalidHandleTypes: null,
16511
16512     /**
16513      * An associative array of ids for elements that will be ignored if clicked
16514      * @property invalidHandleIds
16515      * @type {string: string}
16516      */
16517     invalidHandleIds: null,
16518
16519     /**
16520      * An indexted array of css class names for elements that will be ignored
16521      * if clicked.
16522      * @property invalidHandleClasses
16523      * @type string[]
16524      */
16525     invalidHandleClasses: null,
16526
16527     /**
16528      * The linked element's absolute X position at the time the drag was
16529      * started
16530      * @property startPageX
16531      * @type int
16532      * @private
16533      */
16534     startPageX: 0,
16535
16536     /**
16537      * The linked element's absolute X position at the time the drag was
16538      * started
16539      * @property startPageY
16540      * @type int
16541      * @private
16542      */
16543     startPageY: 0,
16544
16545     /**
16546      * The group defines a logical collection of DragDrop objects that are
16547      * related.  Instances only get events when interacting with other
16548      * DragDrop object in the same group.  This lets us define multiple
16549      * groups using a single DragDrop subclass if we want.
16550      * @property groups
16551      * @type {string: string}
16552      */
16553     groups: null,
16554
16555     /**
16556      * Individual drag/drop instances can be locked.  This will prevent
16557      * onmousedown start drag.
16558      * @property locked
16559      * @type boolean
16560      * @private
16561      */
16562     locked: false,
16563
16564     /**
16565      * Lock this instance
16566      * @method lock
16567      */
16568     lock: function() { this.locked = true; },
16569
16570     /**
16571      * Unlock this instace
16572      * @method unlock
16573      */
16574     unlock: function() { this.locked = false; },
16575
16576     /**
16577      * By default, all insances can be a drop target.  This can be disabled by
16578      * setting isTarget to false.
16579      * @method isTarget
16580      * @type boolean
16581      */
16582     isTarget: true,
16583
16584     /**
16585      * The padding configured for this drag and drop object for calculating
16586      * the drop zone intersection with this object.
16587      * @method padding
16588      * @type int[]
16589      */
16590     padding: null,
16591
16592     /**
16593      * Cached reference to the linked element
16594      * @property _domRef
16595      * @private
16596      */
16597     _domRef: null,
16598
16599     /**
16600      * Internal typeof flag
16601      * @property __ygDragDrop
16602      * @private
16603      */
16604     __ygDragDrop: true,
16605
16606     /**
16607      * Set to true when horizontal contraints are applied
16608      * @property constrainX
16609      * @type boolean
16610      * @private
16611      */
16612     constrainX: false,
16613
16614     /**
16615      * Set to true when vertical contraints are applied
16616      * @property constrainY
16617      * @type boolean
16618      * @private
16619      */
16620     constrainY: false,
16621
16622     /**
16623      * The left constraint
16624      * @property minX
16625      * @type int
16626      * @private
16627      */
16628     minX: 0,
16629
16630     /**
16631      * The right constraint
16632      * @property maxX
16633      * @type int
16634      * @private
16635      */
16636     maxX: 0,
16637
16638     /**
16639      * The up constraint
16640      * @property minY
16641      * @type int
16642      * @type int
16643      * @private
16644      */
16645     minY: 0,
16646
16647     /**
16648      * The down constraint
16649      * @property maxY
16650      * @type int
16651      * @private
16652      */
16653     maxY: 0,
16654
16655     /**
16656      * Maintain offsets when we resetconstraints.  Set to true when you want
16657      * the position of the element relative to its parent to stay the same
16658      * when the page changes
16659      *
16660      * @property maintainOffset
16661      * @type boolean
16662      */
16663     maintainOffset: false,
16664
16665     /**
16666      * Array of pixel locations the element will snap to if we specified a
16667      * horizontal graduation/interval.  This array is generated automatically
16668      * when you define a tick interval.
16669      * @property xTicks
16670      * @type int[]
16671      */
16672     xTicks: null,
16673
16674     /**
16675      * Array of pixel locations the element will snap to if we specified a
16676      * vertical graduation/interval.  This array is generated automatically
16677      * when you define a tick interval.
16678      * @property yTicks
16679      * @type int[]
16680      */
16681     yTicks: null,
16682
16683     /**
16684      * By default the drag and drop instance will only respond to the primary
16685      * button click (left button for a right-handed mouse).  Set to true to
16686      * allow drag and drop to start with any mouse click that is propogated
16687      * by the browser
16688      * @property primaryButtonOnly
16689      * @type boolean
16690      */
16691     primaryButtonOnly: true,
16692
16693     /**
16694      * The availabe property is false until the linked dom element is accessible.
16695      * @property available
16696      * @type boolean
16697      */
16698     available: false,
16699
16700     /**
16701      * By default, drags can only be initiated if the mousedown occurs in the
16702      * region the linked element is.  This is done in part to work around a
16703      * bug in some browsers that mis-report the mousedown if the previous
16704      * mouseup happened outside of the window.  This property is set to true
16705      * if outer handles are defined.
16706      *
16707      * @property hasOuterHandles
16708      * @type boolean
16709      * @default false
16710      */
16711     hasOuterHandles: false,
16712
16713     /**
16714      * Code that executes immediately before the startDrag event
16715      * @method b4StartDrag
16716      * @private
16717      */
16718     b4StartDrag: function(x, y) { },
16719
16720     /**
16721      * Abstract method called after a drag/drop object is clicked
16722      * and the drag or mousedown time thresholds have beeen met.
16723      * @method startDrag
16724      * @param {int} X click location
16725      * @param {int} Y click location
16726      */
16727     startDrag: function(x, y) { /* override this */ },
16728
16729     /**
16730      * Code that executes immediately before the onDrag event
16731      * @method b4Drag
16732      * @private
16733      */
16734     b4Drag: function(e) { },
16735
16736     /**
16737      * Abstract method called during the onMouseMove event while dragging an
16738      * object.
16739      * @method onDrag
16740      * @param {Event} e the mousemove event
16741      */
16742     onDrag: function(e) { /* override this */ },
16743
16744     /**
16745      * Abstract method called when this element fist begins hovering over
16746      * another DragDrop obj
16747      * @method onDragEnter
16748      * @param {Event} e the mousemove event
16749      * @param {String|DragDrop[]} id In POINT mode, the element
16750      * id this is hovering over.  In INTERSECT mode, an array of one or more
16751      * dragdrop items being hovered over.
16752      */
16753     onDragEnter: function(e, id) { /* override this */ },
16754
16755     /**
16756      * Code that executes immediately before the onDragOver event
16757      * @method b4DragOver
16758      * @private
16759      */
16760     b4DragOver: function(e) { },
16761
16762     /**
16763      * Abstract method called when this element is hovering over another
16764      * DragDrop obj
16765      * @method onDragOver
16766      * @param {Event} e the mousemove event
16767      * @param {String|DragDrop[]} id In POINT mode, the element
16768      * id this is hovering over.  In INTERSECT mode, an array of dd items
16769      * being hovered over.
16770      */
16771     onDragOver: function(e, id) { /* override this */ },
16772
16773     /**
16774      * Code that executes immediately before the onDragOut event
16775      * @method b4DragOut
16776      * @private
16777      */
16778     b4DragOut: function(e) { },
16779
16780     /**
16781      * Abstract method called when we are no longer hovering over an element
16782      * @method onDragOut
16783      * @param {Event} e the mousemove event
16784      * @param {String|DragDrop[]} id In POINT mode, the element
16785      * id this was hovering over.  In INTERSECT mode, an array of dd items
16786      * that the mouse is no longer over.
16787      */
16788     onDragOut: function(e, id) { /* override this */ },
16789
16790     /**
16791      * Code that executes immediately before the onDragDrop event
16792      * @method b4DragDrop
16793      * @private
16794      */
16795     b4DragDrop: function(e) { },
16796
16797     /**
16798      * Abstract method called when this item is dropped on another DragDrop
16799      * obj
16800      * @method onDragDrop
16801      * @param {Event} e the mouseup event
16802      * @param {String|DragDrop[]} id In POINT mode, the element
16803      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16804      * was dropped on.
16805      */
16806     onDragDrop: function(e, id) { /* override this */ },
16807
16808     /**
16809      * Abstract method called when this item is dropped on an area with no
16810      * drop target
16811      * @method onInvalidDrop
16812      * @param {Event} e the mouseup event
16813      */
16814     onInvalidDrop: function(e) { /* override this */ },
16815
16816     /**
16817      * Code that executes immediately before the endDrag event
16818      * @method b4EndDrag
16819      * @private
16820      */
16821     b4EndDrag: function(e) { },
16822
16823     /**
16824      * Fired when we are done dragging the object
16825      * @method endDrag
16826      * @param {Event} e the mouseup event
16827      */
16828     endDrag: function(e) { /* override this */ },
16829
16830     /**
16831      * Code executed immediately before the onMouseDown event
16832      * @method b4MouseDown
16833      * @param {Event} e the mousedown event
16834      * @private
16835      */
16836     b4MouseDown: function(e) {  },
16837
16838     /**
16839      * Event handler that fires when a drag/drop obj gets a mousedown
16840      * @method onMouseDown
16841      * @param {Event} e the mousedown event
16842      */
16843     onMouseDown: function(e) { /* override this */ },
16844
16845     /**
16846      * Event handler that fires when a drag/drop obj gets a mouseup
16847      * @method onMouseUp
16848      * @param {Event} e the mouseup event
16849      */
16850     onMouseUp: function(e) { /* override this */ },
16851
16852     /**
16853      * Override the onAvailable method to do what is needed after the initial
16854      * position was determined.
16855      * @method onAvailable
16856      */
16857     onAvailable: function () {
16858     },
16859
16860     /*
16861      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16862      * @type Object
16863      */
16864     defaultPadding : {left:0, right:0, top:0, bottom:0},
16865
16866     /*
16867      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16868  *
16869  * Usage:
16870  <pre><code>
16871  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16872                 { dragElId: "existingProxyDiv" });
16873  dd.startDrag = function(){
16874      this.constrainTo("parent-id");
16875  };
16876  </code></pre>
16877  * Or you can initalize it using the {@link Roo.Element} object:
16878  <pre><code>
16879  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16880      startDrag : function(){
16881          this.constrainTo("parent-id");
16882      }
16883  });
16884  </code></pre>
16885      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16886      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16887      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16888      * an object containing the sides to pad. For example: {right:10, bottom:10}
16889      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16890      */
16891     constrainTo : function(constrainTo, pad, inContent){
16892         if(typeof pad == "number"){
16893             pad = {left: pad, right:pad, top:pad, bottom:pad};
16894         }
16895         pad = pad || this.defaultPadding;
16896         var b = Roo.get(this.getEl()).getBox();
16897         var ce = Roo.get(constrainTo);
16898         var s = ce.getScroll();
16899         var c, cd = ce.dom;
16900         if(cd == document.body){
16901             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16902         }else{
16903             xy = ce.getXY();
16904             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16905         }
16906
16907
16908         var topSpace = b.y - c.y;
16909         var leftSpace = b.x - c.x;
16910
16911         this.resetConstraints();
16912         this.setXConstraint(leftSpace - (pad.left||0), // left
16913                 c.width - leftSpace - b.width - (pad.right||0) //right
16914         );
16915         this.setYConstraint(topSpace - (pad.top||0), //top
16916                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16917         );
16918     },
16919
16920     /**
16921      * Returns a reference to the linked element
16922      * @method getEl
16923      * @return {HTMLElement} the html element
16924      */
16925     getEl: function() {
16926         if (!this._domRef) {
16927             this._domRef = Roo.getDom(this.id);
16928         }
16929
16930         return this._domRef;
16931     },
16932
16933     /**
16934      * Returns a reference to the actual element to drag.  By default this is
16935      * the same as the html element, but it can be assigned to another
16936      * element. An example of this can be found in Roo.dd.DDProxy
16937      * @method getDragEl
16938      * @return {HTMLElement} the html element
16939      */
16940     getDragEl: function() {
16941         return Roo.getDom(this.dragElId);
16942     },
16943
16944     /**
16945      * Sets up the DragDrop object.  Must be called in the constructor of any
16946      * Roo.dd.DragDrop subclass
16947      * @method init
16948      * @param id the id of the linked element
16949      * @param {String} sGroup the group of related items
16950      * @param {object} config configuration attributes
16951      */
16952     init: function(id, sGroup, config) {
16953         this.initTarget(id, sGroup, config);
16954         if (!Roo.isTouch) {
16955             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16956         }
16957         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16958         // Event.on(this.id, "selectstart", Event.preventDefault);
16959     },
16960
16961     /**
16962      * Initializes Targeting functionality only... the object does not
16963      * get a mousedown handler.
16964      * @method initTarget
16965      * @param id the id of the linked element
16966      * @param {String} sGroup the group of related items
16967      * @param {object} config configuration attributes
16968      */
16969     initTarget: function(id, sGroup, config) {
16970
16971         // configuration attributes
16972         this.config = config || {};
16973
16974         // create a local reference to the drag and drop manager
16975         this.DDM = Roo.dd.DDM;
16976         // initialize the groups array
16977         this.groups = {};
16978
16979         // assume that we have an element reference instead of an id if the
16980         // parameter is not a string
16981         if (typeof id !== "string") {
16982             id = Roo.id(id);
16983         }
16984
16985         // set the id
16986         this.id = id;
16987
16988         // add to an interaction group
16989         this.addToGroup((sGroup) ? sGroup : "default");
16990
16991         // We don't want to register this as the handle with the manager
16992         // so we just set the id rather than calling the setter.
16993         this.handleElId = id;
16994
16995         // the linked element is the element that gets dragged by default
16996         this.setDragElId(id);
16997
16998         // by default, clicked anchors will not start drag operations.
16999         this.invalidHandleTypes = { A: "A" };
17000         this.invalidHandleIds = {};
17001         this.invalidHandleClasses = [];
17002
17003         this.applyConfig();
17004
17005         this.handleOnAvailable();
17006     },
17007
17008     /**
17009      * Applies the configuration parameters that were passed into the constructor.
17010      * This is supposed to happen at each level through the inheritance chain.  So
17011      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17012      * DragDrop in order to get all of the parameters that are available in
17013      * each object.
17014      * @method applyConfig
17015      */
17016     applyConfig: function() {
17017
17018         // configurable properties:
17019         //    padding, isTarget, maintainOffset, primaryButtonOnly
17020         this.padding           = this.config.padding || [0, 0, 0, 0];
17021         this.isTarget          = (this.config.isTarget !== false);
17022         this.maintainOffset    = (this.config.maintainOffset);
17023         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17024
17025     },
17026
17027     /**
17028      * Executed when the linked element is available
17029      * @method handleOnAvailable
17030      * @private
17031      */
17032     handleOnAvailable: function() {
17033         this.available = true;
17034         this.resetConstraints();
17035         this.onAvailable();
17036     },
17037
17038      /**
17039      * Configures the padding for the target zone in px.  Effectively expands
17040      * (or reduces) the virtual object size for targeting calculations.
17041      * Supports css-style shorthand; if only one parameter is passed, all sides
17042      * will have that padding, and if only two are passed, the top and bottom
17043      * will have the first param, the left and right the second.
17044      * @method setPadding
17045      * @param {int} iTop    Top pad
17046      * @param {int} iRight  Right pad
17047      * @param {int} iBot    Bot pad
17048      * @param {int} iLeft   Left pad
17049      */
17050     setPadding: function(iTop, iRight, iBot, iLeft) {
17051         // this.padding = [iLeft, iRight, iTop, iBot];
17052         if (!iRight && 0 !== iRight) {
17053             this.padding = [iTop, iTop, iTop, iTop];
17054         } else if (!iBot && 0 !== iBot) {
17055             this.padding = [iTop, iRight, iTop, iRight];
17056         } else {
17057             this.padding = [iTop, iRight, iBot, iLeft];
17058         }
17059     },
17060
17061     /**
17062      * Stores the initial placement of the linked element.
17063      * @method setInitialPosition
17064      * @param {int} diffX   the X offset, default 0
17065      * @param {int} diffY   the Y offset, default 0
17066      */
17067     setInitPosition: function(diffX, diffY) {
17068         var el = this.getEl();
17069
17070         if (!this.DDM.verifyEl(el)) {
17071             return;
17072         }
17073
17074         var dx = diffX || 0;
17075         var dy = diffY || 0;
17076
17077         var p = Dom.getXY( el );
17078
17079         this.initPageX = p[0] - dx;
17080         this.initPageY = p[1] - dy;
17081
17082         this.lastPageX = p[0];
17083         this.lastPageY = p[1];
17084
17085
17086         this.setStartPosition(p);
17087     },
17088
17089     /**
17090      * Sets the start position of the element.  This is set when the obj
17091      * is initialized, the reset when a drag is started.
17092      * @method setStartPosition
17093      * @param pos current position (from previous lookup)
17094      * @private
17095      */
17096     setStartPosition: function(pos) {
17097         var p = pos || Dom.getXY( this.getEl() );
17098         this.deltaSetXY = null;
17099
17100         this.startPageX = p[0];
17101         this.startPageY = p[1];
17102     },
17103
17104     /**
17105      * Add this instance to a group of related drag/drop objects.  All
17106      * instances belong to at least one group, and can belong to as many
17107      * groups as needed.
17108      * @method addToGroup
17109      * @param sGroup {string} the name of the group
17110      */
17111     addToGroup: function(sGroup) {
17112         this.groups[sGroup] = true;
17113         this.DDM.regDragDrop(this, sGroup);
17114     },
17115
17116     /**
17117      * Remove's this instance from the supplied interaction group
17118      * @method removeFromGroup
17119      * @param {string}  sGroup  The group to drop
17120      */
17121     removeFromGroup: function(sGroup) {
17122         if (this.groups[sGroup]) {
17123             delete this.groups[sGroup];
17124         }
17125
17126         this.DDM.removeDDFromGroup(this, sGroup);
17127     },
17128
17129     /**
17130      * Allows you to specify that an element other than the linked element
17131      * will be moved with the cursor during a drag
17132      * @method setDragElId
17133      * @param id {string} the id of the element that will be used to initiate the drag
17134      */
17135     setDragElId: function(id) {
17136         this.dragElId = id;
17137     },
17138
17139     /**
17140      * Allows you to specify a child of the linked element that should be
17141      * used to initiate the drag operation.  An example of this would be if
17142      * you have a content div with text and links.  Clicking anywhere in the
17143      * content area would normally start the drag operation.  Use this method
17144      * to specify that an element inside of the content div is the element
17145      * that starts the drag operation.
17146      * @method setHandleElId
17147      * @param id {string} the id of the element that will be used to
17148      * initiate the drag.
17149      */
17150     setHandleElId: function(id) {
17151         if (typeof id !== "string") {
17152             id = Roo.id(id);
17153         }
17154         this.handleElId = id;
17155         this.DDM.regHandle(this.id, id);
17156     },
17157
17158     /**
17159      * Allows you to set an element outside of the linked element as a drag
17160      * handle
17161      * @method setOuterHandleElId
17162      * @param id the id of the element that will be used to initiate the drag
17163      */
17164     setOuterHandleElId: function(id) {
17165         if (typeof id !== "string") {
17166             id = Roo.id(id);
17167         }
17168         Event.on(id, "mousedown",
17169                 this.handleMouseDown, this);
17170         this.setHandleElId(id);
17171
17172         this.hasOuterHandles = true;
17173     },
17174
17175     /**
17176      * Remove all drag and drop hooks for this element
17177      * @method unreg
17178      */
17179     unreg: function() {
17180         Event.un(this.id, "mousedown",
17181                 this.handleMouseDown);
17182         Event.un(this.id, "touchstart",
17183                 this.handleMouseDown);
17184         this._domRef = null;
17185         this.DDM._remove(this);
17186     },
17187
17188     destroy : function(){
17189         this.unreg();
17190     },
17191
17192     /**
17193      * Returns true if this instance is locked, or the drag drop mgr is locked
17194      * (meaning that all drag/drop is disabled on the page.)
17195      * @method isLocked
17196      * @return {boolean} true if this obj or all drag/drop is locked, else
17197      * false
17198      */
17199     isLocked: function() {
17200         return (this.DDM.isLocked() || this.locked);
17201     },
17202
17203     /**
17204      * Fired when this object is clicked
17205      * @method handleMouseDown
17206      * @param {Event} e
17207      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17208      * @private
17209      */
17210     handleMouseDown: function(e, oDD){
17211      
17212         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17213             //Roo.log('not touch/ button !=0');
17214             return;
17215         }
17216         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17217             return; // double touch..
17218         }
17219         
17220
17221         if (this.isLocked()) {
17222             //Roo.log('locked');
17223             return;
17224         }
17225
17226         this.DDM.refreshCache(this.groups);
17227 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17228         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17229         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17230             //Roo.log('no outer handes or not over target');
17231                 // do nothing.
17232         } else {
17233 //            Roo.log('check validator');
17234             if (this.clickValidator(e)) {
17235 //                Roo.log('validate success');
17236                 // set the initial element position
17237                 this.setStartPosition();
17238
17239
17240                 this.b4MouseDown(e);
17241                 this.onMouseDown(e);
17242
17243                 this.DDM.handleMouseDown(e, this);
17244
17245                 this.DDM.stopEvent(e);
17246             } else {
17247
17248
17249             }
17250         }
17251     },
17252
17253     clickValidator: function(e) {
17254         var target = e.getTarget();
17255         return ( this.isValidHandleChild(target) &&
17256                     (this.id == this.handleElId ||
17257                         this.DDM.handleWasClicked(target, this.id)) );
17258     },
17259
17260     /**
17261      * Allows you to specify a tag name that should not start a drag operation
17262      * when clicked.  This is designed to facilitate embedding links within a
17263      * drag handle that do something other than start the drag.
17264      * @method addInvalidHandleType
17265      * @param {string} tagName the type of element to exclude
17266      */
17267     addInvalidHandleType: function(tagName) {
17268         var type = tagName.toUpperCase();
17269         this.invalidHandleTypes[type] = type;
17270     },
17271
17272     /**
17273      * Lets you to specify an element id for a child of a drag handle
17274      * that should not initiate a drag
17275      * @method addInvalidHandleId
17276      * @param {string} id the element id of the element you wish to ignore
17277      */
17278     addInvalidHandleId: function(id) {
17279         if (typeof id !== "string") {
17280             id = Roo.id(id);
17281         }
17282         this.invalidHandleIds[id] = id;
17283     },
17284
17285     /**
17286      * Lets you specify a css class of elements that will not initiate a drag
17287      * @method addInvalidHandleClass
17288      * @param {string} cssClass the class of the elements you wish to ignore
17289      */
17290     addInvalidHandleClass: function(cssClass) {
17291         this.invalidHandleClasses.push(cssClass);
17292     },
17293
17294     /**
17295      * Unsets an excluded tag name set by addInvalidHandleType
17296      * @method removeInvalidHandleType
17297      * @param {string} tagName the type of element to unexclude
17298      */
17299     removeInvalidHandleType: function(tagName) {
17300         var type = tagName.toUpperCase();
17301         // this.invalidHandleTypes[type] = null;
17302         delete this.invalidHandleTypes[type];
17303     },
17304
17305     /**
17306      * Unsets an invalid handle id
17307      * @method removeInvalidHandleId
17308      * @param {string} id the id of the element to re-enable
17309      */
17310     removeInvalidHandleId: function(id) {
17311         if (typeof id !== "string") {
17312             id = Roo.id(id);
17313         }
17314         delete this.invalidHandleIds[id];
17315     },
17316
17317     /**
17318      * Unsets an invalid css class
17319      * @method removeInvalidHandleClass
17320      * @param {string} cssClass the class of the element(s) you wish to
17321      * re-enable
17322      */
17323     removeInvalidHandleClass: function(cssClass) {
17324         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17325             if (this.invalidHandleClasses[i] == cssClass) {
17326                 delete this.invalidHandleClasses[i];
17327             }
17328         }
17329     },
17330
17331     /**
17332      * Checks the tag exclusion list to see if this click should be ignored
17333      * @method isValidHandleChild
17334      * @param {HTMLElement} node the HTMLElement to evaluate
17335      * @return {boolean} true if this is a valid tag type, false if not
17336      */
17337     isValidHandleChild: function(node) {
17338
17339         var valid = true;
17340         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17341         var nodeName;
17342         try {
17343             nodeName = node.nodeName.toUpperCase();
17344         } catch(e) {
17345             nodeName = node.nodeName;
17346         }
17347         valid = valid && !this.invalidHandleTypes[nodeName];
17348         valid = valid && !this.invalidHandleIds[node.id];
17349
17350         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17351             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17352         }
17353
17354
17355         return valid;
17356
17357     },
17358
17359     /**
17360      * Create the array of horizontal tick marks if an interval was specified
17361      * in setXConstraint().
17362      * @method setXTicks
17363      * @private
17364      */
17365     setXTicks: function(iStartX, iTickSize) {
17366         this.xTicks = [];
17367         this.xTickSize = iTickSize;
17368
17369         var tickMap = {};
17370
17371         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17372             if (!tickMap[i]) {
17373                 this.xTicks[this.xTicks.length] = i;
17374                 tickMap[i] = true;
17375             }
17376         }
17377
17378         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17379             if (!tickMap[i]) {
17380                 this.xTicks[this.xTicks.length] = i;
17381                 tickMap[i] = true;
17382             }
17383         }
17384
17385         this.xTicks.sort(this.DDM.numericSort) ;
17386     },
17387
17388     /**
17389      * Create the array of vertical tick marks if an interval was specified in
17390      * setYConstraint().
17391      * @method setYTicks
17392      * @private
17393      */
17394     setYTicks: function(iStartY, iTickSize) {
17395         this.yTicks = [];
17396         this.yTickSize = iTickSize;
17397
17398         var tickMap = {};
17399
17400         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17401             if (!tickMap[i]) {
17402                 this.yTicks[this.yTicks.length] = i;
17403                 tickMap[i] = true;
17404             }
17405         }
17406
17407         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17408             if (!tickMap[i]) {
17409                 this.yTicks[this.yTicks.length] = i;
17410                 tickMap[i] = true;
17411             }
17412         }
17413
17414         this.yTicks.sort(this.DDM.numericSort) ;
17415     },
17416
17417     /**
17418      * By default, the element can be dragged any place on the screen.  Use
17419      * this method to limit the horizontal travel of the element.  Pass in
17420      * 0,0 for the parameters if you want to lock the drag to the y axis.
17421      * @method setXConstraint
17422      * @param {int} iLeft the number of pixels the element can move to the left
17423      * @param {int} iRight the number of pixels the element can move to the
17424      * right
17425      * @param {int} iTickSize optional parameter for specifying that the
17426      * element
17427      * should move iTickSize pixels at a time.
17428      */
17429     setXConstraint: function(iLeft, iRight, iTickSize) {
17430         this.leftConstraint = iLeft;
17431         this.rightConstraint = iRight;
17432
17433         this.minX = this.initPageX - iLeft;
17434         this.maxX = this.initPageX + iRight;
17435         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17436
17437         this.constrainX = true;
17438     },
17439
17440     /**
17441      * Clears any constraints applied to this instance.  Also clears ticks
17442      * since they can't exist independent of a constraint at this time.
17443      * @method clearConstraints
17444      */
17445     clearConstraints: function() {
17446         this.constrainX = false;
17447         this.constrainY = false;
17448         this.clearTicks();
17449     },
17450
17451     /**
17452      * Clears any tick interval defined for this instance
17453      * @method clearTicks
17454      */
17455     clearTicks: function() {
17456         this.xTicks = null;
17457         this.yTicks = null;
17458         this.xTickSize = 0;
17459         this.yTickSize = 0;
17460     },
17461
17462     /**
17463      * By default, the element can be dragged any place on the screen.  Set
17464      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17465      * parameters if you want to lock the drag to the x axis.
17466      * @method setYConstraint
17467      * @param {int} iUp the number of pixels the element can move up
17468      * @param {int} iDown the number of pixels the element can move down
17469      * @param {int} iTickSize optional parameter for specifying that the
17470      * element should move iTickSize pixels at a time.
17471      */
17472     setYConstraint: function(iUp, iDown, iTickSize) {
17473         this.topConstraint = iUp;
17474         this.bottomConstraint = iDown;
17475
17476         this.minY = this.initPageY - iUp;
17477         this.maxY = this.initPageY + iDown;
17478         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17479
17480         this.constrainY = true;
17481
17482     },
17483
17484     /**
17485      * resetConstraints must be called if you manually reposition a dd element.
17486      * @method resetConstraints
17487      * @param {boolean} maintainOffset
17488      */
17489     resetConstraints: function() {
17490
17491
17492         // Maintain offsets if necessary
17493         if (this.initPageX || this.initPageX === 0) {
17494             // figure out how much this thing has moved
17495             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17496             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17497
17498             this.setInitPosition(dx, dy);
17499
17500         // This is the first time we have detected the element's position
17501         } else {
17502             this.setInitPosition();
17503         }
17504
17505         if (this.constrainX) {
17506             this.setXConstraint( this.leftConstraint,
17507                                  this.rightConstraint,
17508                                  this.xTickSize        );
17509         }
17510
17511         if (this.constrainY) {
17512             this.setYConstraint( this.topConstraint,
17513                                  this.bottomConstraint,
17514                                  this.yTickSize         );
17515         }
17516     },
17517
17518     /**
17519      * Normally the drag element is moved pixel by pixel, but we can specify
17520      * that it move a number of pixels at a time.  This method resolves the
17521      * location when we have it set up like this.
17522      * @method getTick
17523      * @param {int} val where we want to place the object
17524      * @param {int[]} tickArray sorted array of valid points
17525      * @return {int} the closest tick
17526      * @private
17527      */
17528     getTick: function(val, tickArray) {
17529
17530         if (!tickArray) {
17531             // If tick interval is not defined, it is effectively 1 pixel,
17532             // so we return the value passed to us.
17533             return val;
17534         } else if (tickArray[0] >= val) {
17535             // The value is lower than the first tick, so we return the first
17536             // tick.
17537             return tickArray[0];
17538         } else {
17539             for (var i=0, len=tickArray.length; i<len; ++i) {
17540                 var next = i + 1;
17541                 if (tickArray[next] && tickArray[next] >= val) {
17542                     var diff1 = val - tickArray[i];
17543                     var diff2 = tickArray[next] - val;
17544                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17545                 }
17546             }
17547
17548             // The value is larger than the last tick, so we return the last
17549             // tick.
17550             return tickArray[tickArray.length - 1];
17551         }
17552     },
17553
17554     /**
17555      * toString method
17556      * @method toString
17557      * @return {string} string representation of the dd obj
17558      */
17559     toString: function() {
17560         return ("DragDrop " + this.id);
17561     }
17562
17563 });
17564
17565 })();
17566 /*
17567  * Based on:
17568  * Ext JS Library 1.1.1
17569  * Copyright(c) 2006-2007, Ext JS, LLC.
17570  *
17571  * Originally Released Under LGPL - original licence link has changed is not relivant.
17572  *
17573  * Fork - LGPL
17574  * <script type="text/javascript">
17575  */
17576
17577
17578 /**
17579  * The drag and drop utility provides a framework for building drag and drop
17580  * applications.  In addition to enabling drag and drop for specific elements,
17581  * the drag and drop elements are tracked by the manager class, and the
17582  * interactions between the various elements are tracked during the drag and
17583  * the implementing code is notified about these important moments.
17584  */
17585
17586 // Only load the library once.  Rewriting the manager class would orphan
17587 // existing drag and drop instances.
17588 if (!Roo.dd.DragDropMgr) {
17589
17590 /**
17591  * @class Roo.dd.DragDropMgr
17592  * DragDropMgr is a singleton that tracks the element interaction for
17593  * all DragDrop items in the window.  Generally, you will not call
17594  * this class directly, but it does have helper methods that could
17595  * be useful in your DragDrop implementations.
17596  * @singleton
17597  */
17598 Roo.dd.DragDropMgr = function() {
17599
17600     var Event = Roo.EventManager;
17601
17602     return {
17603
17604         /**
17605          * Two dimensional Array of registered DragDrop objects.  The first
17606          * dimension is the DragDrop item group, the second the DragDrop
17607          * object.
17608          * @property ids
17609          * @type {string: string}
17610          * @private
17611          * @static
17612          */
17613         ids: {},
17614
17615         /**
17616          * Array of element ids defined as drag handles.  Used to determine
17617          * if the element that generated the mousedown event is actually the
17618          * handle and not the html element itself.
17619          * @property handleIds
17620          * @type {string: string}
17621          * @private
17622          * @static
17623          */
17624         handleIds: {},
17625
17626         /**
17627          * the DragDrop object that is currently being dragged
17628          * @property dragCurrent
17629          * @type DragDrop
17630          * @private
17631          * @static
17632          **/
17633         dragCurrent: null,
17634
17635         /**
17636          * the DragDrop object(s) that are being hovered over
17637          * @property dragOvers
17638          * @type Array
17639          * @private
17640          * @static
17641          */
17642         dragOvers: {},
17643
17644         /**
17645          * the X distance between the cursor and the object being dragged
17646          * @property deltaX
17647          * @type int
17648          * @private
17649          * @static
17650          */
17651         deltaX: 0,
17652
17653         /**
17654          * the Y distance between the cursor and the object being dragged
17655          * @property deltaY
17656          * @type int
17657          * @private
17658          * @static
17659          */
17660         deltaY: 0,
17661
17662         /**
17663          * Flag to determine if we should prevent the default behavior of the
17664          * events we define. By default this is true, but this can be set to
17665          * false if you need the default behavior (not recommended)
17666          * @property preventDefault
17667          * @type boolean
17668          * @static
17669          */
17670         preventDefault: true,
17671
17672         /**
17673          * Flag to determine if we should stop the propagation of the events
17674          * we generate. This is true by default but you may want to set it to
17675          * false if the html element contains other features that require the
17676          * mouse click.
17677          * @property stopPropagation
17678          * @type boolean
17679          * @static
17680          */
17681         stopPropagation: true,
17682
17683         /**
17684          * Internal flag that is set to true when drag and drop has been
17685          * intialized
17686          * @property initialized
17687          * @private
17688          * @static
17689          */
17690         initalized: false,
17691
17692         /**
17693          * All drag and drop can be disabled.
17694          * @property locked
17695          * @private
17696          * @static
17697          */
17698         locked: false,
17699
17700         /**
17701          * Called the first time an element is registered.
17702          * @method init
17703          * @private
17704          * @static
17705          */
17706         init: function() {
17707             this.initialized = true;
17708         },
17709
17710         /**
17711          * In point mode, drag and drop interaction is defined by the
17712          * location of the cursor during the drag/drop
17713          * @property POINT
17714          * @type int
17715          * @static
17716          */
17717         POINT: 0,
17718
17719         /**
17720          * In intersect mode, drag and drop interactio nis defined by the
17721          * overlap of two or more drag and drop objects.
17722          * @property INTERSECT
17723          * @type int
17724          * @static
17725          */
17726         INTERSECT: 1,
17727
17728         /**
17729          * The current drag and drop mode.  Default: POINT
17730          * @property mode
17731          * @type int
17732          * @static
17733          */
17734         mode: 0,
17735
17736         /**
17737          * Runs method on all drag and drop objects
17738          * @method _execOnAll
17739          * @private
17740          * @static
17741          */
17742         _execOnAll: function(sMethod, args) {
17743             for (var i in this.ids) {
17744                 for (var j in this.ids[i]) {
17745                     var oDD = this.ids[i][j];
17746                     if (! this.isTypeOfDD(oDD)) {
17747                         continue;
17748                     }
17749                     oDD[sMethod].apply(oDD, args);
17750                 }
17751             }
17752         },
17753
17754         /**
17755          * Drag and drop initialization.  Sets up the global event handlers
17756          * @method _onLoad
17757          * @private
17758          * @static
17759          */
17760         _onLoad: function() {
17761
17762             this.init();
17763
17764             if (!Roo.isTouch) {
17765                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17766                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17767             }
17768             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17769             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17770             
17771             Event.on(window,   "unload",    this._onUnload, this, true);
17772             Event.on(window,   "resize",    this._onResize, this, true);
17773             // Event.on(window,   "mouseout",    this._test);
17774
17775         },
17776
17777         /**
17778          * Reset constraints on all drag and drop objs
17779          * @method _onResize
17780          * @private
17781          * @static
17782          */
17783         _onResize: function(e) {
17784             this._execOnAll("resetConstraints", []);
17785         },
17786
17787         /**
17788          * Lock all drag and drop functionality
17789          * @method lock
17790          * @static
17791          */
17792         lock: function() { this.locked = true; },
17793
17794         /**
17795          * Unlock all drag and drop functionality
17796          * @method unlock
17797          * @static
17798          */
17799         unlock: function() { this.locked = false; },
17800
17801         /**
17802          * Is drag and drop locked?
17803          * @method isLocked
17804          * @return {boolean} True if drag and drop is locked, false otherwise.
17805          * @static
17806          */
17807         isLocked: function() { return this.locked; },
17808
17809         /**
17810          * Location cache that is set for all drag drop objects when a drag is
17811          * initiated, cleared when the drag is finished.
17812          * @property locationCache
17813          * @private
17814          * @static
17815          */
17816         locationCache: {},
17817
17818         /**
17819          * Set useCache to false if you want to force object the lookup of each
17820          * drag and drop linked element constantly during a drag.
17821          * @property useCache
17822          * @type boolean
17823          * @static
17824          */
17825         useCache: true,
17826
17827         /**
17828          * The number of pixels that the mouse needs to move after the
17829          * mousedown before the drag is initiated.  Default=3;
17830          * @property clickPixelThresh
17831          * @type int
17832          * @static
17833          */
17834         clickPixelThresh: 3,
17835
17836         /**
17837          * The number of milliseconds after the mousedown event to initiate the
17838          * drag if we don't get a mouseup event. Default=1000
17839          * @property clickTimeThresh
17840          * @type int
17841          * @static
17842          */
17843         clickTimeThresh: 350,
17844
17845         /**
17846          * Flag that indicates that either the drag pixel threshold or the
17847          * mousdown time threshold has been met
17848          * @property dragThreshMet
17849          * @type boolean
17850          * @private
17851          * @static
17852          */
17853         dragThreshMet: false,
17854
17855         /**
17856          * Timeout used for the click time threshold
17857          * @property clickTimeout
17858          * @type Object
17859          * @private
17860          * @static
17861          */
17862         clickTimeout: null,
17863
17864         /**
17865          * The X position of the mousedown event stored for later use when a
17866          * drag threshold is met.
17867          * @property startX
17868          * @type int
17869          * @private
17870          * @static
17871          */
17872         startX: 0,
17873
17874         /**
17875          * The Y position of the mousedown event stored for later use when a
17876          * drag threshold is met.
17877          * @property startY
17878          * @type int
17879          * @private
17880          * @static
17881          */
17882         startY: 0,
17883
17884         /**
17885          * Each DragDrop instance must be registered with the DragDropMgr.
17886          * This is executed in DragDrop.init()
17887          * @method regDragDrop
17888          * @param {DragDrop} oDD the DragDrop object to register
17889          * @param {String} sGroup the name of the group this element belongs to
17890          * @static
17891          */
17892         regDragDrop: function(oDD, sGroup) {
17893             if (!this.initialized) { this.init(); }
17894
17895             if (!this.ids[sGroup]) {
17896                 this.ids[sGroup] = {};
17897             }
17898             this.ids[sGroup][oDD.id] = oDD;
17899         },
17900
17901         /**
17902          * Removes the supplied dd instance from the supplied group. Executed
17903          * by DragDrop.removeFromGroup, so don't call this function directly.
17904          * @method removeDDFromGroup
17905          * @private
17906          * @static
17907          */
17908         removeDDFromGroup: function(oDD, sGroup) {
17909             if (!this.ids[sGroup]) {
17910                 this.ids[sGroup] = {};
17911             }
17912
17913             var obj = this.ids[sGroup];
17914             if (obj && obj[oDD.id]) {
17915                 delete obj[oDD.id];
17916             }
17917         },
17918
17919         /**
17920          * Unregisters a drag and drop item.  This is executed in
17921          * DragDrop.unreg, use that method instead of calling this directly.
17922          * @method _remove
17923          * @private
17924          * @static
17925          */
17926         _remove: function(oDD) {
17927             for (var g in oDD.groups) {
17928                 if (g && this.ids[g][oDD.id]) {
17929                     delete this.ids[g][oDD.id];
17930                 }
17931             }
17932             delete this.handleIds[oDD.id];
17933         },
17934
17935         /**
17936          * Each DragDrop handle element must be registered.  This is done
17937          * automatically when executing DragDrop.setHandleElId()
17938          * @method regHandle
17939          * @param {String} sDDId the DragDrop id this element is a handle for
17940          * @param {String} sHandleId the id of the element that is the drag
17941          * handle
17942          * @static
17943          */
17944         regHandle: function(sDDId, sHandleId) {
17945             if (!this.handleIds[sDDId]) {
17946                 this.handleIds[sDDId] = {};
17947             }
17948             this.handleIds[sDDId][sHandleId] = sHandleId;
17949         },
17950
17951         /**
17952          * Utility function to determine if a given element has been
17953          * registered as a drag drop item.
17954          * @method isDragDrop
17955          * @param {String} id the element id to check
17956          * @return {boolean} true if this element is a DragDrop item,
17957          * false otherwise
17958          * @static
17959          */
17960         isDragDrop: function(id) {
17961             return ( this.getDDById(id) ) ? true : false;
17962         },
17963
17964         /**
17965          * Returns the drag and drop instances that are in all groups the
17966          * passed in instance belongs to.
17967          * @method getRelated
17968          * @param {DragDrop} p_oDD the obj to get related data for
17969          * @param {boolean} bTargetsOnly if true, only return targetable objs
17970          * @return {DragDrop[]} the related instances
17971          * @static
17972          */
17973         getRelated: function(p_oDD, bTargetsOnly) {
17974             var oDDs = [];
17975             for (var i in p_oDD.groups) {
17976                 for (j in this.ids[i]) {
17977                     var dd = this.ids[i][j];
17978                     if (! this.isTypeOfDD(dd)) {
17979                         continue;
17980                     }
17981                     if (!bTargetsOnly || dd.isTarget) {
17982                         oDDs[oDDs.length] = dd;
17983                     }
17984                 }
17985             }
17986
17987             return oDDs;
17988         },
17989
17990         /**
17991          * Returns true if the specified dd target is a legal target for
17992          * the specifice drag obj
17993          * @method isLegalTarget
17994          * @param {DragDrop} the drag obj
17995          * @param {DragDrop} the target
17996          * @return {boolean} true if the target is a legal target for the
17997          * dd obj
17998          * @static
17999          */
18000         isLegalTarget: function (oDD, oTargetDD) {
18001             var targets = this.getRelated(oDD, true);
18002             for (var i=0, len=targets.length;i<len;++i) {
18003                 if (targets[i].id == oTargetDD.id) {
18004                     return true;
18005                 }
18006             }
18007
18008             return false;
18009         },
18010
18011         /**
18012          * My goal is to be able to transparently determine if an object is
18013          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18014          * returns "object", oDD.constructor.toString() always returns
18015          * "DragDrop" and not the name of the subclass.  So for now it just
18016          * evaluates a well-known variable in DragDrop.
18017          * @method isTypeOfDD
18018          * @param {Object} the object to evaluate
18019          * @return {boolean} true if typeof oDD = DragDrop
18020          * @static
18021          */
18022         isTypeOfDD: function (oDD) {
18023             return (oDD && oDD.__ygDragDrop);
18024         },
18025
18026         /**
18027          * Utility function to determine if a given element has been
18028          * registered as a drag drop handle for the given Drag Drop object.
18029          * @method isHandle
18030          * @param {String} id the element id to check
18031          * @return {boolean} true if this element is a DragDrop handle, false
18032          * otherwise
18033          * @static
18034          */
18035         isHandle: function(sDDId, sHandleId) {
18036             return ( this.handleIds[sDDId] &&
18037                             this.handleIds[sDDId][sHandleId] );
18038         },
18039
18040         /**
18041          * Returns the DragDrop instance for a given id
18042          * @method getDDById
18043          * @param {String} id the id of the DragDrop object
18044          * @return {DragDrop} the drag drop object, null if it is not found
18045          * @static
18046          */
18047         getDDById: function(id) {
18048             for (var i in this.ids) {
18049                 if (this.ids[i][id]) {
18050                     return this.ids[i][id];
18051                 }
18052             }
18053             return null;
18054         },
18055
18056         /**
18057          * Fired after a registered DragDrop object gets the mousedown event.
18058          * Sets up the events required to track the object being dragged
18059          * @method handleMouseDown
18060          * @param {Event} e the event
18061          * @param oDD the DragDrop object being dragged
18062          * @private
18063          * @static
18064          */
18065         handleMouseDown: function(e, oDD) {
18066             if(Roo.QuickTips){
18067                 Roo.QuickTips.disable();
18068             }
18069             this.currentTarget = e.getTarget();
18070
18071             this.dragCurrent = oDD;
18072
18073             var el = oDD.getEl();
18074
18075             // track start position
18076             this.startX = e.getPageX();
18077             this.startY = e.getPageY();
18078
18079             this.deltaX = this.startX - el.offsetLeft;
18080             this.deltaY = this.startY - el.offsetTop;
18081
18082             this.dragThreshMet = false;
18083
18084             this.clickTimeout = setTimeout(
18085                     function() {
18086                         var DDM = Roo.dd.DDM;
18087                         DDM.startDrag(DDM.startX, DDM.startY);
18088                     },
18089                     this.clickTimeThresh );
18090         },
18091
18092         /**
18093          * Fired when either the drag pixel threshol or the mousedown hold
18094          * time threshold has been met.
18095          * @method startDrag
18096          * @param x {int} the X position of the original mousedown
18097          * @param y {int} the Y position of the original mousedown
18098          * @static
18099          */
18100         startDrag: function(x, y) {
18101             clearTimeout(this.clickTimeout);
18102             if (this.dragCurrent) {
18103                 this.dragCurrent.b4StartDrag(x, y);
18104                 this.dragCurrent.startDrag(x, y);
18105             }
18106             this.dragThreshMet = true;
18107         },
18108
18109         /**
18110          * Internal function to handle the mouseup event.  Will be invoked
18111          * from the context of the document.
18112          * @method handleMouseUp
18113          * @param {Event} e the event
18114          * @private
18115          * @static
18116          */
18117         handleMouseUp: function(e) {
18118
18119             if(Roo.QuickTips){
18120                 Roo.QuickTips.enable();
18121             }
18122             if (! this.dragCurrent) {
18123                 return;
18124             }
18125
18126             clearTimeout(this.clickTimeout);
18127
18128             if (this.dragThreshMet) {
18129                 this.fireEvents(e, true);
18130             } else {
18131             }
18132
18133             this.stopDrag(e);
18134
18135             this.stopEvent(e);
18136         },
18137
18138         /**
18139          * Utility to stop event propagation and event default, if these
18140          * features are turned on.
18141          * @method stopEvent
18142          * @param {Event} e the event as returned by this.getEvent()
18143          * @static
18144          */
18145         stopEvent: function(e){
18146             if(this.stopPropagation) {
18147                 e.stopPropagation();
18148             }
18149
18150             if (this.preventDefault) {
18151                 e.preventDefault();
18152             }
18153         },
18154
18155         /**
18156          * Internal function to clean up event handlers after the drag
18157          * operation is complete
18158          * @method stopDrag
18159          * @param {Event} e the event
18160          * @private
18161          * @static
18162          */
18163         stopDrag: function(e) {
18164             // Fire the drag end event for the item that was dragged
18165             if (this.dragCurrent) {
18166                 if (this.dragThreshMet) {
18167                     this.dragCurrent.b4EndDrag(e);
18168                     this.dragCurrent.endDrag(e);
18169                 }
18170
18171                 this.dragCurrent.onMouseUp(e);
18172             }
18173
18174             this.dragCurrent = null;
18175             this.dragOvers = {};
18176         },
18177
18178         /**
18179          * Internal function to handle the mousemove event.  Will be invoked
18180          * from the context of the html element.
18181          *
18182          * @TODO figure out what we can do about mouse events lost when the
18183          * user drags objects beyond the window boundary.  Currently we can
18184          * detect this in internet explorer by verifying that the mouse is
18185          * down during the mousemove event.  Firefox doesn't give us the
18186          * button state on the mousemove event.
18187          * @method handleMouseMove
18188          * @param {Event} e the event
18189          * @private
18190          * @static
18191          */
18192         handleMouseMove: function(e) {
18193             if (! this.dragCurrent) {
18194                 return true;
18195             }
18196
18197             // var button = e.which || e.button;
18198
18199             // check for IE mouseup outside of page boundary
18200             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18201                 this.stopEvent(e);
18202                 return this.handleMouseUp(e);
18203             }
18204
18205             if (!this.dragThreshMet) {
18206                 var diffX = Math.abs(this.startX - e.getPageX());
18207                 var diffY = Math.abs(this.startY - e.getPageY());
18208                 if (diffX > this.clickPixelThresh ||
18209                             diffY > this.clickPixelThresh) {
18210                     this.startDrag(this.startX, this.startY);
18211                 }
18212             }
18213
18214             if (this.dragThreshMet) {
18215                 this.dragCurrent.b4Drag(e);
18216                 this.dragCurrent.onDrag(e);
18217                 if(!this.dragCurrent.moveOnly){
18218                     this.fireEvents(e, false);
18219                 }
18220             }
18221
18222             this.stopEvent(e);
18223
18224             return true;
18225         },
18226
18227         /**
18228          * Iterates over all of the DragDrop elements to find ones we are
18229          * hovering over or dropping on
18230          * @method fireEvents
18231          * @param {Event} e the event
18232          * @param {boolean} isDrop is this a drop op or a mouseover op?
18233          * @private
18234          * @static
18235          */
18236         fireEvents: function(e, isDrop) {
18237             var dc = this.dragCurrent;
18238
18239             // If the user did the mouse up outside of the window, we could
18240             // get here even though we have ended the drag.
18241             if (!dc || dc.isLocked()) {
18242                 return;
18243             }
18244
18245             var pt = e.getPoint();
18246
18247             // cache the previous dragOver array
18248             var oldOvers = [];
18249
18250             var outEvts   = [];
18251             var overEvts  = [];
18252             var dropEvts  = [];
18253             var enterEvts = [];
18254
18255             // Check to see if the object(s) we were hovering over is no longer
18256             // being hovered over so we can fire the onDragOut event
18257             for (var i in this.dragOvers) {
18258
18259                 var ddo = this.dragOvers[i];
18260
18261                 if (! this.isTypeOfDD(ddo)) {
18262                     continue;
18263                 }
18264
18265                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18266                     outEvts.push( ddo );
18267                 }
18268
18269                 oldOvers[i] = true;
18270                 delete this.dragOvers[i];
18271             }
18272
18273             for (var sGroup in dc.groups) {
18274
18275                 if ("string" != typeof sGroup) {
18276                     continue;
18277                 }
18278
18279                 for (i in this.ids[sGroup]) {
18280                     var oDD = this.ids[sGroup][i];
18281                     if (! this.isTypeOfDD(oDD)) {
18282                         continue;
18283                     }
18284
18285                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18286                         if (this.isOverTarget(pt, oDD, this.mode)) {
18287                             // look for drop interactions
18288                             if (isDrop) {
18289                                 dropEvts.push( oDD );
18290                             // look for drag enter and drag over interactions
18291                             } else {
18292
18293                                 // initial drag over: dragEnter fires
18294                                 if (!oldOvers[oDD.id]) {
18295                                     enterEvts.push( oDD );
18296                                 // subsequent drag overs: dragOver fires
18297                                 } else {
18298                                     overEvts.push( oDD );
18299                                 }
18300
18301                                 this.dragOvers[oDD.id] = oDD;
18302                             }
18303                         }
18304                     }
18305                 }
18306             }
18307
18308             if (this.mode) {
18309                 if (outEvts.length) {
18310                     dc.b4DragOut(e, outEvts);
18311                     dc.onDragOut(e, outEvts);
18312                 }
18313
18314                 if (enterEvts.length) {
18315                     dc.onDragEnter(e, enterEvts);
18316                 }
18317
18318                 if (overEvts.length) {
18319                     dc.b4DragOver(e, overEvts);
18320                     dc.onDragOver(e, overEvts);
18321                 }
18322
18323                 if (dropEvts.length) {
18324                     dc.b4DragDrop(e, dropEvts);
18325                     dc.onDragDrop(e, dropEvts);
18326                 }
18327
18328             } else {
18329                 // fire dragout events
18330                 var len = 0;
18331                 for (i=0, len=outEvts.length; i<len; ++i) {
18332                     dc.b4DragOut(e, outEvts[i].id);
18333                     dc.onDragOut(e, outEvts[i].id);
18334                 }
18335
18336                 // fire enter events
18337                 for (i=0,len=enterEvts.length; i<len; ++i) {
18338                     // dc.b4DragEnter(e, oDD.id);
18339                     dc.onDragEnter(e, enterEvts[i].id);
18340                 }
18341
18342                 // fire over events
18343                 for (i=0,len=overEvts.length; i<len; ++i) {
18344                     dc.b4DragOver(e, overEvts[i].id);
18345                     dc.onDragOver(e, overEvts[i].id);
18346                 }
18347
18348                 // fire drop events
18349                 for (i=0, len=dropEvts.length; i<len; ++i) {
18350                     dc.b4DragDrop(e, dropEvts[i].id);
18351                     dc.onDragDrop(e, dropEvts[i].id);
18352                 }
18353
18354             }
18355
18356             // notify about a drop that did not find a target
18357             if (isDrop && !dropEvts.length) {
18358                 dc.onInvalidDrop(e);
18359             }
18360
18361         },
18362
18363         /**
18364          * Helper function for getting the best match from the list of drag
18365          * and drop objects returned by the drag and drop events when we are
18366          * in INTERSECT mode.  It returns either the first object that the
18367          * cursor is over, or the object that has the greatest overlap with
18368          * the dragged element.
18369          * @method getBestMatch
18370          * @param  {DragDrop[]} dds The array of drag and drop objects
18371          * targeted
18372          * @return {DragDrop}       The best single match
18373          * @static
18374          */
18375         getBestMatch: function(dds) {
18376             var winner = null;
18377             // Return null if the input is not what we expect
18378             //if (!dds || !dds.length || dds.length == 0) {
18379                // winner = null;
18380             // If there is only one item, it wins
18381             //} else if (dds.length == 1) {
18382
18383             var len = dds.length;
18384
18385             if (len == 1) {
18386                 winner = dds[0];
18387             } else {
18388                 // Loop through the targeted items
18389                 for (var i=0; i<len; ++i) {
18390                     var dd = dds[i];
18391                     // If the cursor is over the object, it wins.  If the
18392                     // cursor is over multiple matches, the first one we come
18393                     // to wins.
18394                     if (dd.cursorIsOver) {
18395                         winner = dd;
18396                         break;
18397                     // Otherwise the object with the most overlap wins
18398                     } else {
18399                         if (!winner ||
18400                             winner.overlap.getArea() < dd.overlap.getArea()) {
18401                             winner = dd;
18402                         }
18403                     }
18404                 }
18405             }
18406
18407             return winner;
18408         },
18409
18410         /**
18411          * Refreshes the cache of the top-left and bottom-right points of the
18412          * drag and drop objects in the specified group(s).  This is in the
18413          * format that is stored in the drag and drop instance, so typical
18414          * usage is:
18415          * <code>
18416          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18417          * </code>
18418          * Alternatively:
18419          * <code>
18420          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18421          * </code>
18422          * @TODO this really should be an indexed array.  Alternatively this
18423          * method could accept both.
18424          * @method refreshCache
18425          * @param {Object} groups an associative array of groups to refresh
18426          * @static
18427          */
18428         refreshCache: function(groups) {
18429             for (var sGroup in groups) {
18430                 if ("string" != typeof sGroup) {
18431                     continue;
18432                 }
18433                 for (var i in this.ids[sGroup]) {
18434                     var oDD = this.ids[sGroup][i];
18435
18436                     if (this.isTypeOfDD(oDD)) {
18437                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18438                         var loc = this.getLocation(oDD);
18439                         if (loc) {
18440                             this.locationCache[oDD.id] = loc;
18441                         } else {
18442                             delete this.locationCache[oDD.id];
18443                             // this will unregister the drag and drop object if
18444                             // the element is not in a usable state
18445                             // oDD.unreg();
18446                         }
18447                     }
18448                 }
18449             }
18450         },
18451
18452         /**
18453          * This checks to make sure an element exists and is in the DOM.  The
18454          * main purpose is to handle cases where innerHTML is used to remove
18455          * drag and drop objects from the DOM.  IE provides an 'unspecified
18456          * error' when trying to access the offsetParent of such an element
18457          * @method verifyEl
18458          * @param {HTMLElement} el the element to check
18459          * @return {boolean} true if the element looks usable
18460          * @static
18461          */
18462         verifyEl: function(el) {
18463             if (el) {
18464                 var parent;
18465                 if(Roo.isIE){
18466                     try{
18467                         parent = el.offsetParent;
18468                     }catch(e){}
18469                 }else{
18470                     parent = el.offsetParent;
18471                 }
18472                 if (parent) {
18473                     return true;
18474                 }
18475             }
18476
18477             return false;
18478         },
18479
18480         /**
18481          * Returns a Region object containing the drag and drop element's position
18482          * and size, including the padding configured for it
18483          * @method getLocation
18484          * @param {DragDrop} oDD the drag and drop object to get the
18485          *                       location for
18486          * @return {Roo.lib.Region} a Region object representing the total area
18487          *                             the element occupies, including any padding
18488          *                             the instance is configured for.
18489          * @static
18490          */
18491         getLocation: function(oDD) {
18492             if (! this.isTypeOfDD(oDD)) {
18493                 return null;
18494             }
18495
18496             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18497
18498             try {
18499                 pos= Roo.lib.Dom.getXY(el);
18500             } catch (e) { }
18501
18502             if (!pos) {
18503                 return null;
18504             }
18505
18506             x1 = pos[0];
18507             x2 = x1 + el.offsetWidth;
18508             y1 = pos[1];
18509             y2 = y1 + el.offsetHeight;
18510
18511             t = y1 - oDD.padding[0];
18512             r = x2 + oDD.padding[1];
18513             b = y2 + oDD.padding[2];
18514             l = x1 - oDD.padding[3];
18515
18516             return new Roo.lib.Region( t, r, b, l );
18517         },
18518
18519         /**
18520          * Checks the cursor location to see if it over the target
18521          * @method isOverTarget
18522          * @param {Roo.lib.Point} pt The point to evaluate
18523          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18524          * @return {boolean} true if the mouse is over the target
18525          * @private
18526          * @static
18527          */
18528         isOverTarget: function(pt, oTarget, intersect) {
18529             // use cache if available
18530             var loc = this.locationCache[oTarget.id];
18531             if (!loc || !this.useCache) {
18532                 loc = this.getLocation(oTarget);
18533                 this.locationCache[oTarget.id] = loc;
18534
18535             }
18536
18537             if (!loc) {
18538                 return false;
18539             }
18540
18541             oTarget.cursorIsOver = loc.contains( pt );
18542
18543             // DragDrop is using this as a sanity check for the initial mousedown
18544             // in this case we are done.  In POINT mode, if the drag obj has no
18545             // contraints, we are also done. Otherwise we need to evaluate the
18546             // location of the target as related to the actual location of the
18547             // dragged element.
18548             var dc = this.dragCurrent;
18549             if (!dc || !dc.getTargetCoord ||
18550                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18551                 return oTarget.cursorIsOver;
18552             }
18553
18554             oTarget.overlap = null;
18555
18556             // Get the current location of the drag element, this is the
18557             // location of the mouse event less the delta that represents
18558             // where the original mousedown happened on the element.  We
18559             // need to consider constraints and ticks as well.
18560             var pos = dc.getTargetCoord(pt.x, pt.y);
18561
18562             var el = dc.getDragEl();
18563             var curRegion = new Roo.lib.Region( pos.y,
18564                                                    pos.x + el.offsetWidth,
18565                                                    pos.y + el.offsetHeight,
18566                                                    pos.x );
18567
18568             var overlap = curRegion.intersect(loc);
18569
18570             if (overlap) {
18571                 oTarget.overlap = overlap;
18572                 return (intersect) ? true : oTarget.cursorIsOver;
18573             } else {
18574                 return false;
18575             }
18576         },
18577
18578         /**
18579          * unload event handler
18580          * @method _onUnload
18581          * @private
18582          * @static
18583          */
18584         _onUnload: function(e, me) {
18585             Roo.dd.DragDropMgr.unregAll();
18586         },
18587
18588         /**
18589          * Cleans up the drag and drop events and objects.
18590          * @method unregAll
18591          * @private
18592          * @static
18593          */
18594         unregAll: function() {
18595
18596             if (this.dragCurrent) {
18597                 this.stopDrag();
18598                 this.dragCurrent = null;
18599             }
18600
18601             this._execOnAll("unreg", []);
18602
18603             for (i in this.elementCache) {
18604                 delete this.elementCache[i];
18605             }
18606
18607             this.elementCache = {};
18608             this.ids = {};
18609         },
18610
18611         /**
18612          * A cache of DOM elements
18613          * @property elementCache
18614          * @private
18615          * @static
18616          */
18617         elementCache: {},
18618
18619         /**
18620          * Get the wrapper for the DOM element specified
18621          * @method getElWrapper
18622          * @param {String} id the id of the element to get
18623          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18624          * @private
18625          * @deprecated This wrapper isn't that useful
18626          * @static
18627          */
18628         getElWrapper: function(id) {
18629             var oWrapper = this.elementCache[id];
18630             if (!oWrapper || !oWrapper.el) {
18631                 oWrapper = this.elementCache[id] =
18632                     new this.ElementWrapper(Roo.getDom(id));
18633             }
18634             return oWrapper;
18635         },
18636
18637         /**
18638          * Returns the actual DOM element
18639          * @method getElement
18640          * @param {String} id the id of the elment to get
18641          * @return {Object} The element
18642          * @deprecated use Roo.getDom instead
18643          * @static
18644          */
18645         getElement: function(id) {
18646             return Roo.getDom(id);
18647         },
18648
18649         /**
18650          * Returns the style property for the DOM element (i.e.,
18651          * document.getElById(id).style)
18652          * @method getCss
18653          * @param {String} id the id of the elment to get
18654          * @return {Object} The style property of the element
18655          * @deprecated use Roo.getDom instead
18656          * @static
18657          */
18658         getCss: function(id) {
18659             var el = Roo.getDom(id);
18660             return (el) ? el.style : null;
18661         },
18662
18663         /**
18664          * Inner class for cached elements
18665          * @class DragDropMgr.ElementWrapper
18666          * @for DragDropMgr
18667          * @private
18668          * @deprecated
18669          */
18670         ElementWrapper: function(el) {
18671                 /**
18672                  * The element
18673                  * @property el
18674                  */
18675                 this.el = el || null;
18676                 /**
18677                  * The element id
18678                  * @property id
18679                  */
18680                 this.id = this.el && el.id;
18681                 /**
18682                  * A reference to the style property
18683                  * @property css
18684                  */
18685                 this.css = this.el && el.style;
18686             },
18687
18688         /**
18689          * Returns the X position of an html element
18690          * @method getPosX
18691          * @param el the element for which to get the position
18692          * @return {int} the X coordinate
18693          * @for DragDropMgr
18694          * @deprecated use Roo.lib.Dom.getX instead
18695          * @static
18696          */
18697         getPosX: function(el) {
18698             return Roo.lib.Dom.getX(el);
18699         },
18700
18701         /**
18702          * Returns the Y position of an html element
18703          * @method getPosY
18704          * @param el the element for which to get the position
18705          * @return {int} the Y coordinate
18706          * @deprecated use Roo.lib.Dom.getY instead
18707          * @static
18708          */
18709         getPosY: function(el) {
18710             return Roo.lib.Dom.getY(el);
18711         },
18712
18713         /**
18714          * Swap two nodes.  In IE, we use the native method, for others we
18715          * emulate the IE behavior
18716          * @method swapNode
18717          * @param n1 the first node to swap
18718          * @param n2 the other node to swap
18719          * @static
18720          */
18721         swapNode: function(n1, n2) {
18722             if (n1.swapNode) {
18723                 n1.swapNode(n2);
18724             } else {
18725                 var p = n2.parentNode;
18726                 var s = n2.nextSibling;
18727
18728                 if (s == n1) {
18729                     p.insertBefore(n1, n2);
18730                 } else if (n2 == n1.nextSibling) {
18731                     p.insertBefore(n2, n1);
18732                 } else {
18733                     n1.parentNode.replaceChild(n2, n1);
18734                     p.insertBefore(n1, s);
18735                 }
18736             }
18737         },
18738
18739         /**
18740          * Returns the current scroll position
18741          * @method getScroll
18742          * @private
18743          * @static
18744          */
18745         getScroll: function () {
18746             var t, l, dde=document.documentElement, db=document.body;
18747             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18748                 t = dde.scrollTop;
18749                 l = dde.scrollLeft;
18750             } else if (db) {
18751                 t = db.scrollTop;
18752                 l = db.scrollLeft;
18753             } else {
18754
18755             }
18756             return { top: t, left: l };
18757         },
18758
18759         /**
18760          * Returns the specified element style property
18761          * @method getStyle
18762          * @param {HTMLElement} el          the element
18763          * @param {string}      styleProp   the style property
18764          * @return {string} The value of the style property
18765          * @deprecated use Roo.lib.Dom.getStyle
18766          * @static
18767          */
18768         getStyle: function(el, styleProp) {
18769             return Roo.fly(el).getStyle(styleProp);
18770         },
18771
18772         /**
18773          * Gets the scrollTop
18774          * @method getScrollTop
18775          * @return {int} the document's scrollTop
18776          * @static
18777          */
18778         getScrollTop: function () { return this.getScroll().top; },
18779
18780         /**
18781          * Gets the scrollLeft
18782          * @method getScrollLeft
18783          * @return {int} the document's scrollTop
18784          * @static
18785          */
18786         getScrollLeft: function () { return this.getScroll().left; },
18787
18788         /**
18789          * Sets the x/y position of an element to the location of the
18790          * target element.
18791          * @method moveToEl
18792          * @param {HTMLElement} moveEl      The element to move
18793          * @param {HTMLElement} targetEl    The position reference element
18794          * @static
18795          */
18796         moveToEl: function (moveEl, targetEl) {
18797             var aCoord = Roo.lib.Dom.getXY(targetEl);
18798             Roo.lib.Dom.setXY(moveEl, aCoord);
18799         },
18800
18801         /**
18802          * Numeric array sort function
18803          * @method numericSort
18804          * @static
18805          */
18806         numericSort: function(a, b) { return (a - b); },
18807
18808         /**
18809          * Internal counter
18810          * @property _timeoutCount
18811          * @private
18812          * @static
18813          */
18814         _timeoutCount: 0,
18815
18816         /**
18817          * Trying to make the load order less important.  Without this we get
18818          * an error if this file is loaded before the Event Utility.
18819          * @method _addListeners
18820          * @private
18821          * @static
18822          */
18823         _addListeners: function() {
18824             var DDM = Roo.dd.DDM;
18825             if ( Roo.lib.Event && document ) {
18826                 DDM._onLoad();
18827             } else {
18828                 if (DDM._timeoutCount > 2000) {
18829                 } else {
18830                     setTimeout(DDM._addListeners, 10);
18831                     if (document && document.body) {
18832                         DDM._timeoutCount += 1;
18833                     }
18834                 }
18835             }
18836         },
18837
18838         /**
18839          * Recursively searches the immediate parent and all child nodes for
18840          * the handle element in order to determine wheter or not it was
18841          * clicked.
18842          * @method handleWasClicked
18843          * @param node the html element to inspect
18844          * @static
18845          */
18846         handleWasClicked: function(node, id) {
18847             if (this.isHandle(id, node.id)) {
18848                 return true;
18849             } else {
18850                 // check to see if this is a text node child of the one we want
18851                 var p = node.parentNode;
18852
18853                 while (p) {
18854                     if (this.isHandle(id, p.id)) {
18855                         return true;
18856                     } else {
18857                         p = p.parentNode;
18858                     }
18859                 }
18860             }
18861
18862             return false;
18863         }
18864
18865     };
18866
18867 }();
18868
18869 // shorter alias, save a few bytes
18870 Roo.dd.DDM = Roo.dd.DragDropMgr;
18871 Roo.dd.DDM._addListeners();
18872
18873 }/*
18874  * Based on:
18875  * Ext JS Library 1.1.1
18876  * Copyright(c) 2006-2007, Ext JS, LLC.
18877  *
18878  * Originally Released Under LGPL - original licence link has changed is not relivant.
18879  *
18880  * Fork - LGPL
18881  * <script type="text/javascript">
18882  */
18883
18884 /**
18885  * @class Roo.dd.DD
18886  * A DragDrop implementation where the linked element follows the
18887  * mouse cursor during a drag.
18888  * @extends Roo.dd.DragDrop
18889  * @constructor
18890  * @param {String} id the id of the linked element
18891  * @param {String} sGroup the group of related DragDrop items
18892  * @param {object} config an object containing configurable attributes
18893  *                Valid properties for DD:
18894  *                    scroll
18895  */
18896 Roo.dd.DD = function(id, sGroup, config) {
18897     if (id) {
18898         this.init(id, sGroup, config);
18899     }
18900 };
18901
18902 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18903
18904     /**
18905      * When set to true, the utility automatically tries to scroll the browser
18906      * window wehn a drag and drop element is dragged near the viewport boundary.
18907      * Defaults to true.
18908      * @property scroll
18909      * @type boolean
18910      */
18911     scroll: true,
18912
18913     /**
18914      * Sets the pointer offset to the distance between the linked element's top
18915      * left corner and the location the element was clicked
18916      * @method autoOffset
18917      * @param {int} iPageX the X coordinate of the click
18918      * @param {int} iPageY the Y coordinate of the click
18919      */
18920     autoOffset: function(iPageX, iPageY) {
18921         var x = iPageX - this.startPageX;
18922         var y = iPageY - this.startPageY;
18923         this.setDelta(x, y);
18924     },
18925
18926     /**
18927      * Sets the pointer offset.  You can call this directly to force the
18928      * offset to be in a particular location (e.g., pass in 0,0 to set it
18929      * to the center of the object)
18930      * @method setDelta
18931      * @param {int} iDeltaX the distance from the left
18932      * @param {int} iDeltaY the distance from the top
18933      */
18934     setDelta: function(iDeltaX, iDeltaY) {
18935         this.deltaX = iDeltaX;
18936         this.deltaY = iDeltaY;
18937     },
18938
18939     /**
18940      * Sets the drag element to the location of the mousedown or click event,
18941      * maintaining the cursor location relative to the location on the element
18942      * that was clicked.  Override this if you want to place the element in a
18943      * location other than where the cursor is.
18944      * @method setDragElPos
18945      * @param {int} iPageX the X coordinate of the mousedown or drag event
18946      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18947      */
18948     setDragElPos: function(iPageX, iPageY) {
18949         // the first time we do this, we are going to check to make sure
18950         // the element has css positioning
18951
18952         var el = this.getDragEl();
18953         this.alignElWithMouse(el, iPageX, iPageY);
18954     },
18955
18956     /**
18957      * Sets the element to the location of the mousedown or click event,
18958      * maintaining the cursor location relative to the location on the element
18959      * that was clicked.  Override this if you want to place the element in a
18960      * location other than where the cursor is.
18961      * @method alignElWithMouse
18962      * @param {HTMLElement} el the element to move
18963      * @param {int} iPageX the X coordinate of the mousedown or drag event
18964      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18965      */
18966     alignElWithMouse: function(el, iPageX, iPageY) {
18967         var oCoord = this.getTargetCoord(iPageX, iPageY);
18968         var fly = el.dom ? el : Roo.fly(el);
18969         if (!this.deltaSetXY) {
18970             var aCoord = [oCoord.x, oCoord.y];
18971             fly.setXY(aCoord);
18972             var newLeft = fly.getLeft(true);
18973             var newTop  = fly.getTop(true);
18974             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18975         } else {
18976             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18977         }
18978
18979         this.cachePosition(oCoord.x, oCoord.y);
18980         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18981         return oCoord;
18982     },
18983
18984     /**
18985      * Saves the most recent position so that we can reset the constraints and
18986      * tick marks on-demand.  We need to know this so that we can calculate the
18987      * number of pixels the element is offset from its original position.
18988      * @method cachePosition
18989      * @param iPageX the current x position (optional, this just makes it so we
18990      * don't have to look it up again)
18991      * @param iPageY the current y position (optional, this just makes it so we
18992      * don't have to look it up again)
18993      */
18994     cachePosition: function(iPageX, iPageY) {
18995         if (iPageX) {
18996             this.lastPageX = iPageX;
18997             this.lastPageY = iPageY;
18998         } else {
18999             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19000             this.lastPageX = aCoord[0];
19001             this.lastPageY = aCoord[1];
19002         }
19003     },
19004
19005     /**
19006      * Auto-scroll the window if the dragged object has been moved beyond the
19007      * visible window boundary.
19008      * @method autoScroll
19009      * @param {int} x the drag element's x position
19010      * @param {int} y the drag element's y position
19011      * @param {int} h the height of the drag element
19012      * @param {int} w the width of the drag element
19013      * @private
19014      */
19015     autoScroll: function(x, y, h, w) {
19016
19017         if (this.scroll) {
19018             // The client height
19019             var clientH = Roo.lib.Dom.getViewWidth();
19020
19021             // The client width
19022             var clientW = Roo.lib.Dom.getViewHeight();
19023
19024             // The amt scrolled down
19025             var st = this.DDM.getScrollTop();
19026
19027             // The amt scrolled right
19028             var sl = this.DDM.getScrollLeft();
19029
19030             // Location of the bottom of the element
19031             var bot = h + y;
19032
19033             // Location of the right of the element
19034             var right = w + x;
19035
19036             // The distance from the cursor to the bottom of the visible area,
19037             // adjusted so that we don't scroll if the cursor is beyond the
19038             // element drag constraints
19039             var toBot = (clientH + st - y - this.deltaY);
19040
19041             // The distance from the cursor to the right of the visible area
19042             var toRight = (clientW + sl - x - this.deltaX);
19043
19044
19045             // How close to the edge the cursor must be before we scroll
19046             // var thresh = (document.all) ? 100 : 40;
19047             var thresh = 40;
19048
19049             // How many pixels to scroll per autoscroll op.  This helps to reduce
19050             // clunky scrolling. IE is more sensitive about this ... it needs this
19051             // value to be higher.
19052             var scrAmt = (document.all) ? 80 : 30;
19053
19054             // Scroll down if we are near the bottom of the visible page and the
19055             // obj extends below the crease
19056             if ( bot > clientH && toBot < thresh ) {
19057                 window.scrollTo(sl, st + scrAmt);
19058             }
19059
19060             // Scroll up if the window is scrolled down and the top of the object
19061             // goes above the top border
19062             if ( y < st && st > 0 && y - st < thresh ) {
19063                 window.scrollTo(sl, st - scrAmt);
19064             }
19065
19066             // Scroll right if the obj is beyond the right border and the cursor is
19067             // near the border.
19068             if ( right > clientW && toRight < thresh ) {
19069                 window.scrollTo(sl + scrAmt, st);
19070             }
19071
19072             // Scroll left if the window has been scrolled to the right and the obj
19073             // extends past the left border
19074             if ( x < sl && sl > 0 && x - sl < thresh ) {
19075                 window.scrollTo(sl - scrAmt, st);
19076             }
19077         }
19078     },
19079
19080     /**
19081      * Finds the location the element should be placed if we want to move
19082      * it to where the mouse location less the click offset would place us.
19083      * @method getTargetCoord
19084      * @param {int} iPageX the X coordinate of the click
19085      * @param {int} iPageY the Y coordinate of the click
19086      * @return an object that contains the coordinates (Object.x and Object.y)
19087      * @private
19088      */
19089     getTargetCoord: function(iPageX, iPageY) {
19090
19091
19092         var x = iPageX - this.deltaX;
19093         var y = iPageY - this.deltaY;
19094
19095         if (this.constrainX) {
19096             if (x < this.minX) { x = this.minX; }
19097             if (x > this.maxX) { x = this.maxX; }
19098         }
19099
19100         if (this.constrainY) {
19101             if (y < this.minY) { y = this.minY; }
19102             if (y > this.maxY) { y = this.maxY; }
19103         }
19104
19105         x = this.getTick(x, this.xTicks);
19106         y = this.getTick(y, this.yTicks);
19107
19108
19109         return {x:x, y:y};
19110     },
19111
19112     /*
19113      * Sets up config options specific to this class. Overrides
19114      * Roo.dd.DragDrop, but all versions of this method through the
19115      * inheritance chain are called
19116      */
19117     applyConfig: function() {
19118         Roo.dd.DD.superclass.applyConfig.call(this);
19119         this.scroll = (this.config.scroll !== false);
19120     },
19121
19122     /*
19123      * Event that fires prior to the onMouseDown event.  Overrides
19124      * Roo.dd.DragDrop.
19125      */
19126     b4MouseDown: function(e) {
19127         // this.resetConstraints();
19128         this.autoOffset(e.getPageX(),
19129                             e.getPageY());
19130     },
19131
19132     /*
19133      * Event that fires prior to the onDrag event.  Overrides
19134      * Roo.dd.DragDrop.
19135      */
19136     b4Drag: function(e) {
19137         this.setDragElPos(e.getPageX(),
19138                             e.getPageY());
19139     },
19140
19141     toString: function() {
19142         return ("DD " + this.id);
19143     }
19144
19145     //////////////////////////////////////////////////////////////////////////
19146     // Debugging ygDragDrop events that can be overridden
19147     //////////////////////////////////////////////////////////////////////////
19148     /*
19149     startDrag: function(x, y) {
19150     },
19151
19152     onDrag: function(e) {
19153     },
19154
19155     onDragEnter: function(e, id) {
19156     },
19157
19158     onDragOver: function(e, id) {
19159     },
19160
19161     onDragOut: function(e, id) {
19162     },
19163
19164     onDragDrop: function(e, id) {
19165     },
19166
19167     endDrag: function(e) {
19168     }
19169
19170     */
19171
19172 });/*
19173  * Based on:
19174  * Ext JS Library 1.1.1
19175  * Copyright(c) 2006-2007, Ext JS, LLC.
19176  *
19177  * Originally Released Under LGPL - original licence link has changed is not relivant.
19178  *
19179  * Fork - LGPL
19180  * <script type="text/javascript">
19181  */
19182
19183 /**
19184  * @class Roo.dd.DDProxy
19185  * A DragDrop implementation that inserts an empty, bordered div into
19186  * the document that follows the cursor during drag operations.  At the time of
19187  * the click, the frame div is resized to the dimensions of the linked html
19188  * element, and moved to the exact location of the linked element.
19189  *
19190  * References to the "frame" element refer to the single proxy element that
19191  * was created to be dragged in place of all DDProxy elements on the
19192  * page.
19193  *
19194  * @extends Roo.dd.DD
19195  * @constructor
19196  * @param {String} id the id of the linked html element
19197  * @param {String} sGroup the group of related DragDrop objects
19198  * @param {object} config an object containing configurable attributes
19199  *                Valid properties for DDProxy in addition to those in DragDrop:
19200  *                   resizeFrame, centerFrame, dragElId
19201  */
19202 Roo.dd.DDProxy = function(id, sGroup, config) {
19203     if (id) {
19204         this.init(id, sGroup, config);
19205         this.initFrame();
19206     }
19207 };
19208
19209 /**
19210  * The default drag frame div id
19211  * @property Roo.dd.DDProxy.dragElId
19212  * @type String
19213  * @static
19214  */
19215 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19216
19217 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19218
19219     /**
19220      * By default we resize the drag frame to be the same size as the element
19221      * we want to drag (this is to get the frame effect).  We can turn it off
19222      * if we want a different behavior.
19223      * @property resizeFrame
19224      * @type boolean
19225      */
19226     resizeFrame: true,
19227
19228     /**
19229      * By default the frame is positioned exactly where the drag element is, so
19230      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19231      * you do not have constraints on the obj is to have the drag frame centered
19232      * around the cursor.  Set centerFrame to true for this effect.
19233      * @property centerFrame
19234      * @type boolean
19235      */
19236     centerFrame: false,
19237
19238     /**
19239      * Creates the proxy element if it does not yet exist
19240      * @method createFrame
19241      */
19242     createFrame: function() {
19243         var self = this;
19244         var body = document.body;
19245
19246         if (!body || !body.firstChild) {
19247             setTimeout( function() { self.createFrame(); }, 50 );
19248             return;
19249         }
19250
19251         var div = this.getDragEl();
19252
19253         if (!div) {
19254             div    = document.createElement("div");
19255             div.id = this.dragElId;
19256             var s  = div.style;
19257
19258             s.position   = "absolute";
19259             s.visibility = "hidden";
19260             s.cursor     = "move";
19261             s.border     = "2px solid #aaa";
19262             s.zIndex     = 999;
19263
19264             // appendChild can blow up IE if invoked prior to the window load event
19265             // while rendering a table.  It is possible there are other scenarios
19266             // that would cause this to happen as well.
19267             body.insertBefore(div, body.firstChild);
19268         }
19269     },
19270
19271     /**
19272      * Initialization for the drag frame element.  Must be called in the
19273      * constructor of all subclasses
19274      * @method initFrame
19275      */
19276     initFrame: function() {
19277         this.createFrame();
19278     },
19279
19280     applyConfig: function() {
19281         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19282
19283         this.resizeFrame = (this.config.resizeFrame !== false);
19284         this.centerFrame = (this.config.centerFrame);
19285         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19286     },
19287
19288     /**
19289      * Resizes the drag frame to the dimensions of the clicked object, positions
19290      * it over the object, and finally displays it
19291      * @method showFrame
19292      * @param {int} iPageX X click position
19293      * @param {int} iPageY Y click position
19294      * @private
19295      */
19296     showFrame: function(iPageX, iPageY) {
19297         var el = this.getEl();
19298         var dragEl = this.getDragEl();
19299         var s = dragEl.style;
19300
19301         this._resizeProxy();
19302
19303         if (this.centerFrame) {
19304             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19305                            Math.round(parseInt(s.height, 10)/2) );
19306         }
19307
19308         this.setDragElPos(iPageX, iPageY);
19309
19310         Roo.fly(dragEl).show();
19311     },
19312
19313     /**
19314      * The proxy is automatically resized to the dimensions of the linked
19315      * element when a drag is initiated, unless resizeFrame is set to false
19316      * @method _resizeProxy
19317      * @private
19318      */
19319     _resizeProxy: function() {
19320         if (this.resizeFrame) {
19321             var el = this.getEl();
19322             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19323         }
19324     },
19325
19326     // overrides Roo.dd.DragDrop
19327     b4MouseDown: function(e) {
19328         var x = e.getPageX();
19329         var y = e.getPageY();
19330         this.autoOffset(x, y);
19331         this.setDragElPos(x, y);
19332     },
19333
19334     // overrides Roo.dd.DragDrop
19335     b4StartDrag: function(x, y) {
19336         // show the drag frame
19337         this.showFrame(x, y);
19338     },
19339
19340     // overrides Roo.dd.DragDrop
19341     b4EndDrag: function(e) {
19342         Roo.fly(this.getDragEl()).hide();
19343     },
19344
19345     // overrides Roo.dd.DragDrop
19346     // By default we try to move the element to the last location of the frame.
19347     // This is so that the default behavior mirrors that of Roo.dd.DD.
19348     endDrag: function(e) {
19349
19350         var lel = this.getEl();
19351         var del = this.getDragEl();
19352
19353         // Show the drag frame briefly so we can get its position
19354         del.style.visibility = "";
19355
19356         this.beforeMove();
19357         // Hide the linked element before the move to get around a Safari
19358         // rendering bug.
19359         lel.style.visibility = "hidden";
19360         Roo.dd.DDM.moveToEl(lel, del);
19361         del.style.visibility = "hidden";
19362         lel.style.visibility = "";
19363
19364         this.afterDrag();
19365     },
19366
19367     beforeMove : function(){
19368
19369     },
19370
19371     afterDrag : function(){
19372
19373     },
19374
19375     toString: function() {
19376         return ("DDProxy " + this.id);
19377     }
19378
19379 });
19380 /*
19381  * Based on:
19382  * Ext JS Library 1.1.1
19383  * Copyright(c) 2006-2007, Ext JS, LLC.
19384  *
19385  * Originally Released Under LGPL - original licence link has changed is not relivant.
19386  *
19387  * Fork - LGPL
19388  * <script type="text/javascript">
19389  */
19390
19391  /**
19392  * @class Roo.dd.DDTarget
19393  * A DragDrop implementation that does not move, but can be a drop
19394  * target.  You would get the same result by simply omitting implementation
19395  * for the event callbacks, but this way we reduce the processing cost of the
19396  * event listener and the callbacks.
19397  * @extends Roo.dd.DragDrop
19398  * @constructor
19399  * @param {String} id the id of the element that is a drop target
19400  * @param {String} sGroup the group of related DragDrop objects
19401  * @param {object} config an object containing configurable attributes
19402  *                 Valid properties for DDTarget in addition to those in
19403  *                 DragDrop:
19404  *                    none
19405  */
19406 Roo.dd.DDTarget = function(id, sGroup, config) {
19407     if (id) {
19408         this.initTarget(id, sGroup, config);
19409     }
19410     if (config.listeners || config.events) { 
19411        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19412             listeners : config.listeners || {}, 
19413             events : config.events || {} 
19414         });    
19415     }
19416 };
19417
19418 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19419 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19420     toString: function() {
19421         return ("DDTarget " + this.id);
19422     }
19423 });
19424 /*
19425  * Based on:
19426  * Ext JS Library 1.1.1
19427  * Copyright(c) 2006-2007, Ext JS, LLC.
19428  *
19429  * Originally Released Under LGPL - original licence link has changed is not relivant.
19430  *
19431  * Fork - LGPL
19432  * <script type="text/javascript">
19433  */
19434  
19435
19436 /**
19437  * @class Roo.dd.ScrollManager
19438  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19439  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19440  * @singleton
19441  */
19442 Roo.dd.ScrollManager = function(){
19443     var ddm = Roo.dd.DragDropMgr;
19444     var els = {};
19445     var dragEl = null;
19446     var proc = {};
19447     
19448     
19449     
19450     var onStop = function(e){
19451         dragEl = null;
19452         clearProc();
19453     };
19454     
19455     var triggerRefresh = function(){
19456         if(ddm.dragCurrent){
19457              ddm.refreshCache(ddm.dragCurrent.groups);
19458         }
19459     };
19460     
19461     var doScroll = function(){
19462         if(ddm.dragCurrent){
19463             var dds = Roo.dd.ScrollManager;
19464             if(!dds.animate){
19465                 if(proc.el.scroll(proc.dir, dds.increment)){
19466                     triggerRefresh();
19467                 }
19468             }else{
19469                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19470             }
19471         }
19472     };
19473     
19474     var clearProc = function(){
19475         if(proc.id){
19476             clearInterval(proc.id);
19477         }
19478         proc.id = 0;
19479         proc.el = null;
19480         proc.dir = "";
19481     };
19482     
19483     var startProc = function(el, dir){
19484          Roo.log('scroll startproc');
19485         clearProc();
19486         proc.el = el;
19487         proc.dir = dir;
19488         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19489     };
19490     
19491     var onFire = function(e, isDrop){
19492        
19493         if(isDrop || !ddm.dragCurrent){ return; }
19494         var dds = Roo.dd.ScrollManager;
19495         if(!dragEl || dragEl != ddm.dragCurrent){
19496             dragEl = ddm.dragCurrent;
19497             // refresh regions on drag start
19498             dds.refreshCache();
19499         }
19500         
19501         var xy = Roo.lib.Event.getXY(e);
19502         var pt = new Roo.lib.Point(xy[0], xy[1]);
19503         for(var id in els){
19504             var el = els[id], r = el._region;
19505             if(r && r.contains(pt) && el.isScrollable()){
19506                 if(r.bottom - pt.y <= dds.thresh){
19507                     if(proc.el != el){
19508                         startProc(el, "down");
19509                     }
19510                     return;
19511                 }else if(r.right - pt.x <= dds.thresh){
19512                     if(proc.el != el){
19513                         startProc(el, "left");
19514                     }
19515                     return;
19516                 }else if(pt.y - r.top <= dds.thresh){
19517                     if(proc.el != el){
19518                         startProc(el, "up");
19519                     }
19520                     return;
19521                 }else if(pt.x - r.left <= dds.thresh){
19522                     if(proc.el != el){
19523                         startProc(el, "right");
19524                     }
19525                     return;
19526                 }
19527             }
19528         }
19529         clearProc();
19530     };
19531     
19532     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19533     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19534     
19535     return {
19536         /**
19537          * Registers new overflow element(s) to auto scroll
19538          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19539          */
19540         register : function(el){
19541             if(el instanceof Array){
19542                 for(var i = 0, len = el.length; i < len; i++) {
19543                         this.register(el[i]);
19544                 }
19545             }else{
19546                 el = Roo.get(el);
19547                 els[el.id] = el;
19548             }
19549             Roo.dd.ScrollManager.els = els;
19550         },
19551         
19552         /**
19553          * Unregisters overflow element(s) so they are no longer scrolled
19554          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19555          */
19556         unregister : function(el){
19557             if(el instanceof Array){
19558                 for(var i = 0, len = el.length; i < len; i++) {
19559                         this.unregister(el[i]);
19560                 }
19561             }else{
19562                 el = Roo.get(el);
19563                 delete els[el.id];
19564             }
19565         },
19566         
19567         /**
19568          * The number of pixels from the edge of a container the pointer needs to be to 
19569          * trigger scrolling (defaults to 25)
19570          * @type Number
19571          */
19572         thresh : 25,
19573         
19574         /**
19575          * The number of pixels to scroll in each scroll increment (defaults to 50)
19576          * @type Number
19577          */
19578         increment : 100,
19579         
19580         /**
19581          * The frequency of scrolls in milliseconds (defaults to 500)
19582          * @type Number
19583          */
19584         frequency : 500,
19585         
19586         /**
19587          * True to animate the scroll (defaults to true)
19588          * @type Boolean
19589          */
19590         animate: true,
19591         
19592         /**
19593          * The animation duration in seconds - 
19594          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19595          * @type Number
19596          */
19597         animDuration: .4,
19598         
19599         /**
19600          * Manually trigger a cache refresh.
19601          */
19602         refreshCache : function(){
19603             for(var id in els){
19604                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19605                     els[id]._region = els[id].getRegion();
19606                 }
19607             }
19608         }
19609     };
19610 }();/*
19611  * Based on:
19612  * Ext JS Library 1.1.1
19613  * Copyright(c) 2006-2007, Ext JS, LLC.
19614  *
19615  * Originally Released Under LGPL - original licence link has changed is not relivant.
19616  *
19617  * Fork - LGPL
19618  * <script type="text/javascript">
19619  */
19620  
19621
19622 /**
19623  * @class Roo.dd.Registry
19624  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19625  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19626  * @singleton
19627  */
19628 Roo.dd.Registry = function(){
19629     var elements = {}; 
19630     var handles = {}; 
19631     var autoIdSeed = 0;
19632
19633     var getId = function(el, autogen){
19634         if(typeof el == "string"){
19635             return el;
19636         }
19637         var id = el.id;
19638         if(!id && autogen !== false){
19639             id = "roodd-" + (++autoIdSeed);
19640             el.id = id;
19641         }
19642         return id;
19643     };
19644     
19645     return {
19646     /**
19647      * Register a drag drop element
19648      * @param {String|HTMLElement} element The id or DOM node to register
19649      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19650      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19651      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19652      * populated in the data object (if applicable):
19653      * <pre>
19654 Value      Description<br />
19655 ---------  ------------------------------------------<br />
19656 handles    Array of DOM nodes that trigger dragging<br />
19657            for the element being registered<br />
19658 isHandle   True if the element passed in triggers<br />
19659            dragging itself, else false
19660 </pre>
19661      */
19662         register : function(el, data){
19663             data = data || {};
19664             if(typeof el == "string"){
19665                 el = document.getElementById(el);
19666             }
19667             data.ddel = el;
19668             elements[getId(el)] = data;
19669             if(data.isHandle !== false){
19670                 handles[data.ddel.id] = data;
19671             }
19672             if(data.handles){
19673                 var hs = data.handles;
19674                 for(var i = 0, len = hs.length; i < len; i++){
19675                         handles[getId(hs[i])] = data;
19676                 }
19677             }
19678         },
19679
19680     /**
19681      * Unregister a drag drop element
19682      * @param {String|HTMLElement}  element The id or DOM node to unregister
19683      */
19684         unregister : function(el){
19685             var id = getId(el, false);
19686             var data = elements[id];
19687             if(data){
19688                 delete elements[id];
19689                 if(data.handles){
19690                     var hs = data.handles;
19691                     for(var i = 0, len = hs.length; i < len; i++){
19692                         delete handles[getId(hs[i], false)];
19693                     }
19694                 }
19695             }
19696         },
19697
19698     /**
19699      * Returns the handle registered for a DOM Node by id
19700      * @param {String|HTMLElement} id The DOM node or id to look up
19701      * @return {Object} handle The custom handle data
19702      */
19703         getHandle : function(id){
19704             if(typeof id != "string"){ // must be element?
19705                 id = id.id;
19706             }
19707             return handles[id];
19708         },
19709
19710     /**
19711      * Returns the handle that is registered for the DOM node that is the target of the event
19712      * @param {Event} e The event
19713      * @return {Object} handle The custom handle data
19714      */
19715         getHandleFromEvent : function(e){
19716             var t = Roo.lib.Event.getTarget(e);
19717             return t ? handles[t.id] : null;
19718         },
19719
19720     /**
19721      * Returns a custom data object that is registered for a DOM node by id
19722      * @param {String|HTMLElement} id The DOM node or id to look up
19723      * @return {Object} data The custom data
19724      */
19725         getTarget : function(id){
19726             if(typeof id != "string"){ // must be element?
19727                 id = id.id;
19728             }
19729             return elements[id];
19730         },
19731
19732     /**
19733      * Returns a custom data object that is registered for the DOM node that is the target of the event
19734      * @param {Event} e The event
19735      * @return {Object} data The custom data
19736      */
19737         getTargetFromEvent : function(e){
19738             var t = Roo.lib.Event.getTarget(e);
19739             return t ? elements[t.id] || handles[t.id] : null;
19740         }
19741     };
19742 }();/*
19743  * Based on:
19744  * Ext JS Library 1.1.1
19745  * Copyright(c) 2006-2007, Ext JS, LLC.
19746  *
19747  * Originally Released Under LGPL - original licence link has changed is not relivant.
19748  *
19749  * Fork - LGPL
19750  * <script type="text/javascript">
19751  */
19752  
19753
19754 /**
19755  * @class Roo.dd.StatusProxy
19756  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19757  * default drag proxy used by all Roo.dd components.
19758  * @constructor
19759  * @param {Object} config
19760  */
19761 Roo.dd.StatusProxy = function(config){
19762     Roo.apply(this, config);
19763     this.id = this.id || Roo.id();
19764     this.el = new Roo.Layer({
19765         dh: {
19766             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19767                 {tag: "div", cls: "x-dd-drop-icon"},
19768                 {tag: "div", cls: "x-dd-drag-ghost"}
19769             ]
19770         }, 
19771         shadow: !config || config.shadow !== false
19772     });
19773     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19774     this.dropStatus = this.dropNotAllowed;
19775 };
19776
19777 Roo.dd.StatusProxy.prototype = {
19778     /**
19779      * @cfg {String} dropAllowed
19780      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19781      */
19782     dropAllowed : "x-dd-drop-ok",
19783     /**
19784      * @cfg {String} dropNotAllowed
19785      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19786      */
19787     dropNotAllowed : "x-dd-drop-nodrop",
19788
19789     /**
19790      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19791      * over the current target element.
19792      * @param {String} cssClass The css class for the new drop status indicator image
19793      */
19794     setStatus : function(cssClass){
19795         cssClass = cssClass || this.dropNotAllowed;
19796         if(this.dropStatus != cssClass){
19797             this.el.replaceClass(this.dropStatus, cssClass);
19798             this.dropStatus = cssClass;
19799         }
19800     },
19801
19802     /**
19803      * Resets the status indicator to the default dropNotAllowed value
19804      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19805      */
19806     reset : function(clearGhost){
19807         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19808         this.dropStatus = this.dropNotAllowed;
19809         if(clearGhost){
19810             this.ghost.update("");
19811         }
19812     },
19813
19814     /**
19815      * Updates the contents of the ghost element
19816      * @param {String} html The html that will replace the current innerHTML of the ghost element
19817      */
19818     update : function(html){
19819         if(typeof html == "string"){
19820             this.ghost.update(html);
19821         }else{
19822             this.ghost.update("");
19823             html.style.margin = "0";
19824             this.ghost.dom.appendChild(html);
19825         }
19826         // ensure float = none set?? cant remember why though.
19827         var el = this.ghost.dom.firstChild;
19828                 if(el){
19829                         Roo.fly(el).setStyle('float', 'none');
19830                 }
19831     },
19832     
19833     /**
19834      * Returns the underlying proxy {@link Roo.Layer}
19835      * @return {Roo.Layer} el
19836     */
19837     getEl : function(){
19838         return this.el;
19839     },
19840
19841     /**
19842      * Returns the ghost element
19843      * @return {Roo.Element} el
19844      */
19845     getGhost : function(){
19846         return this.ghost;
19847     },
19848
19849     /**
19850      * Hides the proxy
19851      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19852      */
19853     hide : function(clear){
19854         this.el.hide();
19855         if(clear){
19856             this.reset(true);
19857         }
19858     },
19859
19860     /**
19861      * Stops the repair animation if it's currently running
19862      */
19863     stop : function(){
19864         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19865             this.anim.stop();
19866         }
19867     },
19868
19869     /**
19870      * Displays this proxy
19871      */
19872     show : function(){
19873         this.el.show();
19874     },
19875
19876     /**
19877      * Force the Layer to sync its shadow and shim positions to the element
19878      */
19879     sync : function(){
19880         this.el.sync();
19881     },
19882
19883     /**
19884      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19885      * invalid drop operation by the item being dragged.
19886      * @param {Array} xy The XY position of the element ([x, y])
19887      * @param {Function} callback The function to call after the repair is complete
19888      * @param {Object} scope The scope in which to execute the callback
19889      */
19890     repair : function(xy, callback, scope){
19891         this.callback = callback;
19892         this.scope = scope;
19893         if(xy && this.animRepair !== false){
19894             this.el.addClass("x-dd-drag-repair");
19895             this.el.hideUnders(true);
19896             this.anim = this.el.shift({
19897                 duration: this.repairDuration || .5,
19898                 easing: 'easeOut',
19899                 xy: xy,
19900                 stopFx: true,
19901                 callback: this.afterRepair,
19902                 scope: this
19903             });
19904         }else{
19905             this.afterRepair();
19906         }
19907     },
19908
19909     // private
19910     afterRepair : function(){
19911         this.hide(true);
19912         if(typeof this.callback == "function"){
19913             this.callback.call(this.scope || this);
19914         }
19915         this.callback = null;
19916         this.scope = null;
19917     }
19918 };/*
19919  * Based on:
19920  * Ext JS Library 1.1.1
19921  * Copyright(c) 2006-2007, Ext JS, LLC.
19922  *
19923  * Originally Released Under LGPL - original licence link has changed is not relivant.
19924  *
19925  * Fork - LGPL
19926  * <script type="text/javascript">
19927  */
19928
19929 /**
19930  * @class Roo.dd.DragSource
19931  * @extends Roo.dd.DDProxy
19932  * A simple class that provides the basic implementation needed to make any element draggable.
19933  * @constructor
19934  * @param {String/HTMLElement/Element} el The container element
19935  * @param {Object} config
19936  */
19937 Roo.dd.DragSource = function(el, config){
19938     this.el = Roo.get(el);
19939     this.dragData = {};
19940     
19941     Roo.apply(this, config);
19942     
19943     if(!this.proxy){
19944         this.proxy = new Roo.dd.StatusProxy();
19945     }
19946
19947     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19948           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19949     
19950     this.dragging = false;
19951 };
19952
19953 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19954     /**
19955      * @cfg {String} dropAllowed
19956      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19957      */
19958     dropAllowed : "x-dd-drop-ok",
19959     /**
19960      * @cfg {String} dropNotAllowed
19961      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19962      */
19963     dropNotAllowed : "x-dd-drop-nodrop",
19964
19965     /**
19966      * Returns the data object associated with this drag source
19967      * @return {Object} data An object containing arbitrary data
19968      */
19969     getDragData : function(e){
19970         return this.dragData;
19971     },
19972
19973     // private
19974     onDragEnter : function(e, id){
19975         var target = Roo.dd.DragDropMgr.getDDById(id);
19976         this.cachedTarget = target;
19977         if(this.beforeDragEnter(target, e, id) !== false){
19978             if(target.isNotifyTarget){
19979                 var status = target.notifyEnter(this, e, this.dragData);
19980                 this.proxy.setStatus(status);
19981             }else{
19982                 this.proxy.setStatus(this.dropAllowed);
19983             }
19984             
19985             if(this.afterDragEnter){
19986                 /**
19987                  * An empty function by default, but provided so that you can perform a custom action
19988                  * when the dragged item enters the drop target by providing an implementation.
19989                  * @param {Roo.dd.DragDrop} target The drop target
19990                  * @param {Event} e The event object
19991                  * @param {String} id The id of the dragged element
19992                  * @method afterDragEnter
19993                  */
19994                 this.afterDragEnter(target, e, id);
19995             }
19996         }
19997     },
19998
19999     /**
20000      * An empty function by default, but provided so that you can perform a custom action
20001      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20002      * @param {Roo.dd.DragDrop} target The drop target
20003      * @param {Event} e The event object
20004      * @param {String} id The id of the dragged element
20005      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20006      */
20007     beforeDragEnter : function(target, e, id){
20008         return true;
20009     },
20010
20011     // private
20012     alignElWithMouse: function() {
20013         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20014         this.proxy.sync();
20015     },
20016
20017     // private
20018     onDragOver : function(e, id){
20019         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20020         if(this.beforeDragOver(target, e, id) !== false){
20021             if(target.isNotifyTarget){
20022                 var status = target.notifyOver(this, e, this.dragData);
20023                 this.proxy.setStatus(status);
20024             }
20025
20026             if(this.afterDragOver){
20027                 /**
20028                  * An empty function by default, but provided so that you can perform a custom action
20029                  * while the dragged item is over the drop target by providing an implementation.
20030                  * @param {Roo.dd.DragDrop} target The drop target
20031                  * @param {Event} e The event object
20032                  * @param {String} id The id of the dragged element
20033                  * @method afterDragOver
20034                  */
20035                 this.afterDragOver(target, e, id);
20036             }
20037         }
20038     },
20039
20040     /**
20041      * An empty function by default, but provided so that you can perform a custom action
20042      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20043      * @param {Roo.dd.DragDrop} target The drop target
20044      * @param {Event} e The event object
20045      * @param {String} id The id of the dragged element
20046      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20047      */
20048     beforeDragOver : function(target, e, id){
20049         return true;
20050     },
20051
20052     // private
20053     onDragOut : function(e, id){
20054         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20055         if(this.beforeDragOut(target, e, id) !== false){
20056             if(target.isNotifyTarget){
20057                 target.notifyOut(this, e, this.dragData);
20058             }
20059             this.proxy.reset();
20060             if(this.afterDragOut){
20061                 /**
20062                  * An empty function by default, but provided so that you can perform a custom action
20063                  * after the dragged item is dragged out of the target without dropping.
20064                  * @param {Roo.dd.DragDrop} target The drop target
20065                  * @param {Event} e The event object
20066                  * @param {String} id The id of the dragged element
20067                  * @method afterDragOut
20068                  */
20069                 this.afterDragOut(target, e, id);
20070             }
20071         }
20072         this.cachedTarget = null;
20073     },
20074
20075     /**
20076      * An empty function by default, but provided so that you can perform a custom action before the dragged
20077      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20078      * @param {Roo.dd.DragDrop} target The drop target
20079      * @param {Event} e The event object
20080      * @param {String} id The id of the dragged element
20081      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20082      */
20083     beforeDragOut : function(target, e, id){
20084         return true;
20085     },
20086     
20087     // private
20088     onDragDrop : function(e, id){
20089         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20090         if(this.beforeDragDrop(target, e, id) !== false){
20091             if(target.isNotifyTarget){
20092                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20093                     this.onValidDrop(target, e, id);
20094                 }else{
20095                     this.onInvalidDrop(target, e, id);
20096                 }
20097             }else{
20098                 this.onValidDrop(target, e, id);
20099             }
20100             
20101             if(this.afterDragDrop){
20102                 /**
20103                  * An empty function by default, but provided so that you can perform a custom action
20104                  * after a valid drag drop has occurred by providing an implementation.
20105                  * @param {Roo.dd.DragDrop} target The drop target
20106                  * @param {Event} e The event object
20107                  * @param {String} id The id of the dropped element
20108                  * @method afterDragDrop
20109                  */
20110                 this.afterDragDrop(target, e, id);
20111             }
20112         }
20113         delete this.cachedTarget;
20114     },
20115
20116     /**
20117      * An empty function by default, but provided so that you can perform a custom action before the dragged
20118      * item is dropped onto the target and optionally cancel the onDragDrop.
20119      * @param {Roo.dd.DragDrop} target The drop target
20120      * @param {Event} e The event object
20121      * @param {String} id The id of the dragged element
20122      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20123      */
20124     beforeDragDrop : function(target, e, id){
20125         return true;
20126     },
20127
20128     // private
20129     onValidDrop : function(target, e, id){
20130         this.hideProxy();
20131         if(this.afterValidDrop){
20132             /**
20133              * An empty function by default, but provided so that you can perform a custom action
20134              * after a valid drop has occurred by providing an implementation.
20135              * @param {Object} target The target DD 
20136              * @param {Event} e The event object
20137              * @param {String} id The id of the dropped element
20138              * @method afterInvalidDrop
20139              */
20140             this.afterValidDrop(target, e, id);
20141         }
20142     },
20143
20144     // private
20145     getRepairXY : function(e, data){
20146         return this.el.getXY();  
20147     },
20148
20149     // private
20150     onInvalidDrop : function(target, e, id){
20151         this.beforeInvalidDrop(target, e, id);
20152         if(this.cachedTarget){
20153             if(this.cachedTarget.isNotifyTarget){
20154                 this.cachedTarget.notifyOut(this, e, this.dragData);
20155             }
20156             this.cacheTarget = null;
20157         }
20158         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20159
20160         if(this.afterInvalidDrop){
20161             /**
20162              * An empty function by default, but provided so that you can perform a custom action
20163              * after an invalid drop has occurred by providing an implementation.
20164              * @param {Event} e The event object
20165              * @param {String} id The id of the dropped element
20166              * @method afterInvalidDrop
20167              */
20168             this.afterInvalidDrop(e, id);
20169         }
20170     },
20171
20172     // private
20173     afterRepair : function(){
20174         if(Roo.enableFx){
20175             this.el.highlight(this.hlColor || "c3daf9");
20176         }
20177         this.dragging = false;
20178     },
20179
20180     /**
20181      * An empty function by default, but provided so that you can perform a custom action after an invalid
20182      * drop has occurred.
20183      * @param {Roo.dd.DragDrop} target The drop target
20184      * @param {Event} e The event object
20185      * @param {String} id The id of the dragged element
20186      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20187      */
20188     beforeInvalidDrop : function(target, e, id){
20189         return true;
20190     },
20191
20192     // private
20193     handleMouseDown : function(e){
20194         if(this.dragging) {
20195             return;
20196         }
20197         var data = this.getDragData(e);
20198         if(data && this.onBeforeDrag(data, e) !== false){
20199             this.dragData = data;
20200             this.proxy.stop();
20201             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20202         } 
20203     },
20204
20205     /**
20206      * An empty function by default, but provided so that you can perform a custom action before the initial
20207      * drag event begins and optionally cancel it.
20208      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20209      * @param {Event} e The event object
20210      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20211      */
20212     onBeforeDrag : function(data, e){
20213         return true;
20214     },
20215
20216     /**
20217      * An empty function by default, but provided so that you can perform a custom action once the initial
20218      * drag event has begun.  The drag cannot be canceled from this function.
20219      * @param {Number} x The x position of the click on the dragged object
20220      * @param {Number} y The y position of the click on the dragged object
20221      */
20222     onStartDrag : Roo.emptyFn,
20223
20224     // private - YUI override
20225     startDrag : function(x, y){
20226         this.proxy.reset();
20227         this.dragging = true;
20228         this.proxy.update("");
20229         this.onInitDrag(x, y);
20230         this.proxy.show();
20231     },
20232
20233     // private
20234     onInitDrag : function(x, y){
20235         var clone = this.el.dom.cloneNode(true);
20236         clone.id = Roo.id(); // prevent duplicate ids
20237         this.proxy.update(clone);
20238         this.onStartDrag(x, y);
20239         return true;
20240     },
20241
20242     /**
20243      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20244      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20245      */
20246     getProxy : function(){
20247         return this.proxy;  
20248     },
20249
20250     /**
20251      * Hides the drag source's {@link Roo.dd.StatusProxy}
20252      */
20253     hideProxy : function(){
20254         this.proxy.hide();  
20255         this.proxy.reset(true);
20256         this.dragging = false;
20257     },
20258
20259     // private
20260     triggerCacheRefresh : function(){
20261         Roo.dd.DDM.refreshCache(this.groups);
20262     },
20263
20264     // private - override to prevent hiding
20265     b4EndDrag: function(e) {
20266     },
20267
20268     // private - override to prevent moving
20269     endDrag : function(e){
20270         this.onEndDrag(this.dragData, e);
20271     },
20272
20273     // private
20274     onEndDrag : function(data, e){
20275     },
20276     
20277     // private - pin to cursor
20278     autoOffset : function(x, y) {
20279         this.setDelta(-12, -20);
20280     }    
20281 });/*
20282  * Based on:
20283  * Ext JS Library 1.1.1
20284  * Copyright(c) 2006-2007, Ext JS, LLC.
20285  *
20286  * Originally Released Under LGPL - original licence link has changed is not relivant.
20287  *
20288  * Fork - LGPL
20289  * <script type="text/javascript">
20290  */
20291
20292
20293 /**
20294  * @class Roo.dd.DropTarget
20295  * @extends Roo.dd.DDTarget
20296  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20297  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20298  * @constructor
20299  * @param {String/HTMLElement/Element} el The container element
20300  * @param {Object} config
20301  */
20302 Roo.dd.DropTarget = function(el, config){
20303     this.el = Roo.get(el);
20304     
20305     var listeners = false; ;
20306     if (config && config.listeners) {
20307         listeners= config.listeners;
20308         delete config.listeners;
20309     }
20310     Roo.apply(this, config);
20311     
20312     if(this.containerScroll){
20313         Roo.dd.ScrollManager.register(this.el);
20314     }
20315     this.addEvents( {
20316          /**
20317          * @scope Roo.dd.DropTarget
20318          */
20319          
20320          /**
20321          * @event enter
20322          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20323          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20324          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20325          * 
20326          * IMPORTANT : it should set this.overClass and this.dropAllowed
20327          * 
20328          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20329          * @param {Event} e The event
20330          * @param {Object} data An object containing arbitrary data supplied by the drag source
20331          */
20332         "enter" : true,
20333         
20334          /**
20335          * @event over
20336          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20337          * This method will be called on every mouse movement while the drag source is over the drop target.
20338          * This default implementation simply returns the dropAllowed config value.
20339          * 
20340          * IMPORTANT : it should set this.dropAllowed
20341          * 
20342          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20343          * @param {Event} e The event
20344          * @param {Object} data An object containing arbitrary data supplied by the drag source
20345          
20346          */
20347         "over" : true,
20348         /**
20349          * @event out
20350          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20351          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20352          * overClass (if any) from the drop element.
20353          * 
20354          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20355          * @param {Event} e The event
20356          * @param {Object} data An object containing arbitrary data supplied by the drag source
20357          */
20358          "out" : true,
20359          
20360         /**
20361          * @event drop
20362          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20363          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20364          * implementation that does something to process the drop event and returns true so that the drag source's
20365          * repair action does not run.
20366          * 
20367          * IMPORTANT : it should set this.success
20368          * 
20369          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20370          * @param {Event} e The event
20371          * @param {Object} data An object containing arbitrary data supplied by the drag source
20372         */
20373          "drop" : true
20374     });
20375             
20376      
20377     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20378         this.el.dom, 
20379         this.ddGroup || this.group,
20380         {
20381             isTarget: true,
20382             listeners : listeners || {} 
20383            
20384         
20385         }
20386     );
20387
20388 };
20389
20390 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20391     /**
20392      * @cfg {String} overClass
20393      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20394      */
20395      /**
20396      * @cfg {String} ddGroup
20397      * The drag drop group to handle drop events for
20398      */
20399      
20400     /**
20401      * @cfg {String} dropAllowed
20402      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20403      */
20404     dropAllowed : "x-dd-drop-ok",
20405     /**
20406      * @cfg {String} dropNotAllowed
20407      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20408      */
20409     dropNotAllowed : "x-dd-drop-nodrop",
20410     /**
20411      * @cfg {boolean} success
20412      * set this after drop listener.. 
20413      */
20414     success : false,
20415     /**
20416      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20417      * if the drop point is valid for over/enter..
20418      */
20419     valid : false,
20420     // private
20421     isTarget : true,
20422
20423     // private
20424     isNotifyTarget : true,
20425     
20426     /**
20427      * @hide
20428      */
20429     notifyEnter : function(dd, e, data)
20430     {
20431         this.valid = true;
20432         this.fireEvent('enter', dd, e, data);
20433         if(this.overClass){
20434             this.el.addClass(this.overClass);
20435         }
20436         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20437             this.valid ? this.dropAllowed : this.dropNotAllowed
20438         );
20439     },
20440
20441     /**
20442      * @hide
20443      */
20444     notifyOver : function(dd, e, data)
20445     {
20446         this.valid = true;
20447         this.fireEvent('over', dd, e, data);
20448         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20449             this.valid ? this.dropAllowed : this.dropNotAllowed
20450         );
20451     },
20452
20453     /**
20454      * @hide
20455      */
20456     notifyOut : function(dd, e, data)
20457     {
20458         this.fireEvent('out', dd, e, data);
20459         if(this.overClass){
20460             this.el.removeClass(this.overClass);
20461         }
20462     },
20463
20464     /**
20465      * @hide
20466      */
20467     notifyDrop : function(dd, e, data)
20468     {
20469         this.success = false;
20470         this.fireEvent('drop', dd, e, data);
20471         return this.success;
20472     }
20473 });/*
20474  * Based on:
20475  * Ext JS Library 1.1.1
20476  * Copyright(c) 2006-2007, Ext JS, LLC.
20477  *
20478  * Originally Released Under LGPL - original licence link has changed is not relivant.
20479  *
20480  * Fork - LGPL
20481  * <script type="text/javascript">
20482  */
20483
20484
20485 /**
20486  * @class Roo.dd.DragZone
20487  * @extends Roo.dd.DragSource
20488  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20489  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20490  * @constructor
20491  * @param {String/HTMLElement/Element} el The container element
20492  * @param {Object} config
20493  */
20494 Roo.dd.DragZone = function(el, config){
20495     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20496     if(this.containerScroll){
20497         Roo.dd.ScrollManager.register(this.el);
20498     }
20499 };
20500
20501 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20502     /**
20503      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20504      * for auto scrolling during drag operations.
20505      */
20506     /**
20507      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20508      * method after a failed drop (defaults to "c3daf9" - light blue)
20509      */
20510
20511     /**
20512      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20513      * for a valid target to drag based on the mouse down. Override this method
20514      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20515      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20516      * @param {EventObject} e The mouse down event
20517      * @return {Object} The dragData
20518      */
20519     getDragData : function(e){
20520         return Roo.dd.Registry.getHandleFromEvent(e);
20521     },
20522     
20523     /**
20524      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20525      * this.dragData.ddel
20526      * @param {Number} x The x position of the click on the dragged object
20527      * @param {Number} y The y position of the click on the dragged object
20528      * @return {Boolean} true to continue the drag, false to cancel
20529      */
20530     onInitDrag : function(x, y){
20531         this.proxy.update(this.dragData.ddel.cloneNode(true));
20532         this.onStartDrag(x, y);
20533         return true;
20534     },
20535     
20536     /**
20537      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20538      */
20539     afterRepair : function(){
20540         if(Roo.enableFx){
20541             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20542         }
20543         this.dragging = false;
20544     },
20545
20546     /**
20547      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20548      * the XY of this.dragData.ddel
20549      * @param {EventObject} e The mouse up event
20550      * @return {Array} The xy location (e.g. [100, 200])
20551      */
20552     getRepairXY : function(e){
20553         return Roo.Element.fly(this.dragData.ddel).getXY();  
20554     }
20555 });/*
20556  * Based on:
20557  * Ext JS Library 1.1.1
20558  * Copyright(c) 2006-2007, Ext JS, LLC.
20559  *
20560  * Originally Released Under LGPL - original licence link has changed is not relivant.
20561  *
20562  * Fork - LGPL
20563  * <script type="text/javascript">
20564  */
20565 /**
20566  * @class Roo.dd.DropZone
20567  * @extends Roo.dd.DropTarget
20568  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20569  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20570  * @constructor
20571  * @param {String/HTMLElement/Element} el The container element
20572  * @param {Object} config
20573  */
20574 Roo.dd.DropZone = function(el, config){
20575     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20576 };
20577
20578 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20579     /**
20580      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20581      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20582      * provide your own custom lookup.
20583      * @param {Event} e The event
20584      * @return {Object} data The custom data
20585      */
20586     getTargetFromEvent : function(e){
20587         return Roo.dd.Registry.getTargetFromEvent(e);
20588     },
20589
20590     /**
20591      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20592      * that it has registered.  This method has no default implementation and should be overridden to provide
20593      * node-specific processing if necessary.
20594      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20595      * {@link #getTargetFromEvent} for this node)
20596      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20597      * @param {Event} e The event
20598      * @param {Object} data An object containing arbitrary data supplied by the drag source
20599      */
20600     onNodeEnter : function(n, dd, e, data){
20601         
20602     },
20603
20604     /**
20605      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20606      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20607      * overridden to provide the proper feedback.
20608      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20609      * {@link #getTargetFromEvent} for this node)
20610      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20611      * @param {Event} e The event
20612      * @param {Object} data An object containing arbitrary data supplied by the drag source
20613      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20614      * underlying {@link Roo.dd.StatusProxy} can be updated
20615      */
20616     onNodeOver : function(n, dd, e, data){
20617         return this.dropAllowed;
20618     },
20619
20620     /**
20621      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20622      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20623      * node-specific processing if necessary.
20624      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20625      * {@link #getTargetFromEvent} for this node)
20626      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20627      * @param {Event} e The event
20628      * @param {Object} data An object containing arbitrary data supplied by the drag source
20629      */
20630     onNodeOut : function(n, dd, e, data){
20631         
20632     },
20633
20634     /**
20635      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20636      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20637      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20638      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20639      * {@link #getTargetFromEvent} for this node)
20640      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20641      * @param {Event} e The event
20642      * @param {Object} data An object containing arbitrary data supplied by the drag source
20643      * @return {Boolean} True if the drop was valid, else false
20644      */
20645     onNodeDrop : function(n, dd, e, data){
20646         return false;
20647     },
20648
20649     /**
20650      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20651      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20652      * it should be overridden to provide the proper feedback if necessary.
20653      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20654      * @param {Event} e The event
20655      * @param {Object} data An object containing arbitrary data supplied by the drag source
20656      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20657      * underlying {@link Roo.dd.StatusProxy} can be updated
20658      */
20659     onContainerOver : function(dd, e, data){
20660         return this.dropNotAllowed;
20661     },
20662
20663     /**
20664      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20665      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20666      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20667      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20668      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20669      * @param {Event} e The event
20670      * @param {Object} data An object containing arbitrary data supplied by the drag source
20671      * @return {Boolean} True if the drop was valid, else false
20672      */
20673     onContainerDrop : function(dd, e, data){
20674         return false;
20675     },
20676
20677     /**
20678      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20679      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20680      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20681      * you should override this method and provide a custom implementation.
20682      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20683      * @param {Event} e The event
20684      * @param {Object} data An object containing arbitrary data supplied by the drag source
20685      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20686      * underlying {@link Roo.dd.StatusProxy} can be updated
20687      */
20688     notifyEnter : function(dd, e, data){
20689         return this.dropNotAllowed;
20690     },
20691
20692     /**
20693      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20694      * This method will be called on every mouse movement while the drag source is over the drop zone.
20695      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20696      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20697      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20698      * registered node, it will call {@link #onContainerOver}.
20699      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20700      * @param {Event} e The event
20701      * @param {Object} data An object containing arbitrary data supplied by the drag source
20702      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20703      * underlying {@link Roo.dd.StatusProxy} can be updated
20704      */
20705     notifyOver : function(dd, e, data){
20706         var n = this.getTargetFromEvent(e);
20707         if(!n){ // not over valid drop target
20708             if(this.lastOverNode){
20709                 this.onNodeOut(this.lastOverNode, dd, e, data);
20710                 this.lastOverNode = null;
20711             }
20712             return this.onContainerOver(dd, e, data);
20713         }
20714         if(this.lastOverNode != n){
20715             if(this.lastOverNode){
20716                 this.onNodeOut(this.lastOverNode, dd, e, data);
20717             }
20718             this.onNodeEnter(n, dd, e, data);
20719             this.lastOverNode = n;
20720         }
20721         return this.onNodeOver(n, dd, e, data);
20722     },
20723
20724     /**
20725      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20726      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20727      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20728      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20729      * @param {Event} e The event
20730      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20731      */
20732     notifyOut : function(dd, e, data){
20733         if(this.lastOverNode){
20734             this.onNodeOut(this.lastOverNode, dd, e, data);
20735             this.lastOverNode = null;
20736         }
20737     },
20738
20739     /**
20740      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20741      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20742      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20743      * otherwise it will call {@link #onContainerDrop}.
20744      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20745      * @param {Event} e The event
20746      * @param {Object} data An object containing arbitrary data supplied by the drag source
20747      * @return {Boolean} True if the drop was valid, else false
20748      */
20749     notifyDrop : function(dd, e, data){
20750         if(this.lastOverNode){
20751             this.onNodeOut(this.lastOverNode, dd, e, data);
20752             this.lastOverNode = null;
20753         }
20754         var n = this.getTargetFromEvent(e);
20755         return n ?
20756             this.onNodeDrop(n, dd, e, data) :
20757             this.onContainerDrop(dd, e, data);
20758     },
20759
20760     // private
20761     triggerCacheRefresh : function(){
20762         Roo.dd.DDM.refreshCache(this.groups);
20763     }  
20764 });/*
20765  * Based on:
20766  * Ext JS Library 1.1.1
20767  * Copyright(c) 2006-2007, Ext JS, LLC.
20768  *
20769  * Originally Released Under LGPL - original licence link has changed is not relivant.
20770  *
20771  * Fork - LGPL
20772  * <script type="text/javascript">
20773  */
20774
20775
20776 /**
20777  * @class Roo.data.SortTypes
20778  * @singleton
20779  * Defines the default sorting (casting?) comparison functions used when sorting data.
20780  */
20781 Roo.data.SortTypes = {
20782     /**
20783      * Default sort that does nothing
20784      * @param {Mixed} s The value being converted
20785      * @return {Mixed} The comparison value
20786      */
20787     none : function(s){
20788         return s;
20789     },
20790     
20791     /**
20792      * The regular expression used to strip tags
20793      * @type {RegExp}
20794      * @property
20795      */
20796     stripTagsRE : /<\/?[^>]+>/gi,
20797     
20798     /**
20799      * Strips all HTML tags to sort on text only
20800      * @param {Mixed} s The value being converted
20801      * @return {String} The comparison value
20802      */
20803     asText : function(s){
20804         return String(s).replace(this.stripTagsRE, "");
20805     },
20806     
20807     /**
20808      * Strips all HTML tags to sort on text only - Case insensitive
20809      * @param {Mixed} s The value being converted
20810      * @return {String} The comparison value
20811      */
20812     asUCText : function(s){
20813         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20814     },
20815     
20816     /**
20817      * Case insensitive string
20818      * @param {Mixed} s The value being converted
20819      * @return {String} The comparison value
20820      */
20821     asUCString : function(s) {
20822         return String(s).toUpperCase();
20823     },
20824     
20825     /**
20826      * Date sorting
20827      * @param {Mixed} s The value being converted
20828      * @return {Number} The comparison value
20829      */
20830     asDate : function(s) {
20831         if(!s){
20832             return 0;
20833         }
20834         if(s instanceof Date){
20835             return s.getTime();
20836         }
20837         return Date.parse(String(s));
20838     },
20839     
20840     /**
20841      * Float sorting
20842      * @param {Mixed} s The value being converted
20843      * @return {Float} The comparison value
20844      */
20845     asFloat : function(s) {
20846         var val = parseFloat(String(s).replace(/,/g, ""));
20847         if(isNaN(val)) val = 0;
20848         return val;
20849     },
20850     
20851     /**
20852      * Integer sorting
20853      * @param {Mixed} s The value being converted
20854      * @return {Number} The comparison value
20855      */
20856     asInt : function(s) {
20857         var val = parseInt(String(s).replace(/,/g, ""));
20858         if(isNaN(val)) val = 0;
20859         return val;
20860     }
20861 };/*
20862  * Based on:
20863  * Ext JS Library 1.1.1
20864  * Copyright(c) 2006-2007, Ext JS, LLC.
20865  *
20866  * Originally Released Under LGPL - original licence link has changed is not relivant.
20867  *
20868  * Fork - LGPL
20869  * <script type="text/javascript">
20870  */
20871
20872 /**
20873 * @class Roo.data.Record
20874  * Instances of this class encapsulate both record <em>definition</em> information, and record
20875  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20876  * to access Records cached in an {@link Roo.data.Store} object.<br>
20877  * <p>
20878  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20879  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20880  * objects.<br>
20881  * <p>
20882  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20883  * @constructor
20884  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20885  * {@link #create}. The parameters are the same.
20886  * @param {Array} data An associative Array of data values keyed by the field name.
20887  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20888  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20889  * not specified an integer id is generated.
20890  */
20891 Roo.data.Record = function(data, id){
20892     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20893     this.data = data;
20894 };
20895
20896 /**
20897  * Generate a constructor for a specific record layout.
20898  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20899  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20900  * Each field definition object may contain the following properties: <ul>
20901  * <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,
20902  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20903  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20904  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20905  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20906  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20907  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20908  * this may be omitted.</p></li>
20909  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20910  * <ul><li>auto (Default, implies no conversion)</li>
20911  * <li>string</li>
20912  * <li>int</li>
20913  * <li>float</li>
20914  * <li>boolean</li>
20915  * <li>date</li></ul></p></li>
20916  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20917  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20918  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20919  * by the Reader into an object that will be stored in the Record. It is passed the
20920  * following parameters:<ul>
20921  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20922  * </ul></p></li>
20923  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20924  * </ul>
20925  * <br>usage:<br><pre><code>
20926 var TopicRecord = Roo.data.Record.create(
20927     {name: 'title', mapping: 'topic_title'},
20928     {name: 'author', mapping: 'username'},
20929     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20930     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20931     {name: 'lastPoster', mapping: 'user2'},
20932     {name: 'excerpt', mapping: 'post_text'}
20933 );
20934
20935 var myNewRecord = new TopicRecord({
20936     title: 'Do my job please',
20937     author: 'noobie',
20938     totalPosts: 1,
20939     lastPost: new Date(),
20940     lastPoster: 'Animal',
20941     excerpt: 'No way dude!'
20942 });
20943 myStore.add(myNewRecord);
20944 </code></pre>
20945  * @method create
20946  * @static
20947  */
20948 Roo.data.Record.create = function(o){
20949     var f = function(){
20950         f.superclass.constructor.apply(this, arguments);
20951     };
20952     Roo.extend(f, Roo.data.Record);
20953     var p = f.prototype;
20954     p.fields = new Roo.util.MixedCollection(false, function(field){
20955         return field.name;
20956     });
20957     for(var i = 0, len = o.length; i < len; i++){
20958         p.fields.add(new Roo.data.Field(o[i]));
20959     }
20960     f.getField = function(name){
20961         return p.fields.get(name);  
20962     };
20963     return f;
20964 };
20965
20966 Roo.data.Record.AUTO_ID = 1000;
20967 Roo.data.Record.EDIT = 'edit';
20968 Roo.data.Record.REJECT = 'reject';
20969 Roo.data.Record.COMMIT = 'commit';
20970
20971 Roo.data.Record.prototype = {
20972     /**
20973      * Readonly flag - true if this record has been modified.
20974      * @type Boolean
20975      */
20976     dirty : false,
20977     editing : false,
20978     error: null,
20979     modified: null,
20980
20981     // private
20982     join : function(store){
20983         this.store = store;
20984     },
20985
20986     /**
20987      * Set the named field to the specified value.
20988      * @param {String} name The name of the field to set.
20989      * @param {Object} value The value to set the field to.
20990      */
20991     set : function(name, value){
20992         if(this.data[name] == value){
20993             return;
20994         }
20995         this.dirty = true;
20996         if(!this.modified){
20997             this.modified = {};
20998         }
20999         if(typeof this.modified[name] == 'undefined'){
21000             this.modified[name] = this.data[name];
21001         }
21002         this.data[name] = value;
21003         if(!this.editing && this.store){
21004             this.store.afterEdit(this);
21005         }       
21006     },
21007
21008     /**
21009      * Get the value of the named field.
21010      * @param {String} name The name of the field to get the value of.
21011      * @return {Object} The value of the field.
21012      */
21013     get : function(name){
21014         return this.data[name]; 
21015     },
21016
21017     // private
21018     beginEdit : function(){
21019         this.editing = true;
21020         this.modified = {}; 
21021     },
21022
21023     // private
21024     cancelEdit : function(){
21025         this.editing = false;
21026         delete this.modified;
21027     },
21028
21029     // private
21030     endEdit : function(){
21031         this.editing = false;
21032         if(this.dirty && this.store){
21033             this.store.afterEdit(this);
21034         }
21035     },
21036
21037     /**
21038      * Usually called by the {@link Roo.data.Store} which owns the Record.
21039      * Rejects all changes made to the Record since either creation, or the last commit operation.
21040      * Modified fields are reverted to their original values.
21041      * <p>
21042      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21043      * of reject operations.
21044      */
21045     reject : function(){
21046         var m = this.modified;
21047         for(var n in m){
21048             if(typeof m[n] != "function"){
21049                 this.data[n] = m[n];
21050             }
21051         }
21052         this.dirty = false;
21053         delete this.modified;
21054         this.editing = false;
21055         if(this.store){
21056             this.store.afterReject(this);
21057         }
21058     },
21059
21060     /**
21061      * Usually called by the {@link Roo.data.Store} which owns the Record.
21062      * Commits all changes made to the Record since either creation, or the last commit operation.
21063      * <p>
21064      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21065      * of commit operations.
21066      */
21067     commit : function(){
21068         this.dirty = false;
21069         delete this.modified;
21070         this.editing = false;
21071         if(this.store){
21072             this.store.afterCommit(this);
21073         }
21074     },
21075
21076     // private
21077     hasError : function(){
21078         return this.error != null;
21079     },
21080
21081     // private
21082     clearError : function(){
21083         this.error = null;
21084     },
21085
21086     /**
21087      * Creates a copy of this record.
21088      * @param {String} id (optional) A new record id if you don't want to use this record's id
21089      * @return {Record}
21090      */
21091     copy : function(newId) {
21092         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21093     }
21094 };/*
21095  * Based on:
21096  * Ext JS Library 1.1.1
21097  * Copyright(c) 2006-2007, Ext JS, LLC.
21098  *
21099  * Originally Released Under LGPL - original licence link has changed is not relivant.
21100  *
21101  * Fork - LGPL
21102  * <script type="text/javascript">
21103  */
21104
21105
21106
21107 /**
21108  * @class Roo.data.Store
21109  * @extends Roo.util.Observable
21110  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21111  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21112  * <p>
21113  * 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
21114  * has no knowledge of the format of the data returned by the Proxy.<br>
21115  * <p>
21116  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21117  * instances from the data object. These records are cached and made available through accessor functions.
21118  * @constructor
21119  * Creates a new Store.
21120  * @param {Object} config A config object containing the objects needed for the Store to access data,
21121  * and read the data into Records.
21122  */
21123 Roo.data.Store = function(config){
21124     this.data = new Roo.util.MixedCollection(false);
21125     this.data.getKey = function(o){
21126         return o.id;
21127     };
21128     this.baseParams = {};
21129     // private
21130     this.paramNames = {
21131         "start" : "start",
21132         "limit" : "limit",
21133         "sort" : "sort",
21134         "dir" : "dir",
21135         "multisort" : "_multisort"
21136     };
21137
21138     if(config && config.data){
21139         this.inlineData = config.data;
21140         delete config.data;
21141     }
21142
21143     Roo.apply(this, config);
21144     
21145     if(this.reader){ // reader passed
21146         this.reader = Roo.factory(this.reader, Roo.data);
21147         this.reader.xmodule = this.xmodule || false;
21148         if(!this.recordType){
21149             this.recordType = this.reader.recordType;
21150         }
21151         if(this.reader.onMetaChange){
21152             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21153         }
21154     }
21155
21156     if(this.recordType){
21157         this.fields = this.recordType.prototype.fields;
21158     }
21159     this.modified = [];
21160
21161     this.addEvents({
21162         /**
21163          * @event datachanged
21164          * Fires when the data cache has changed, and a widget which is using this Store
21165          * as a Record cache should refresh its view.
21166          * @param {Store} this
21167          */
21168         datachanged : true,
21169         /**
21170          * @event metachange
21171          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21172          * @param {Store} this
21173          * @param {Object} meta The JSON metadata
21174          */
21175         metachange : true,
21176         /**
21177          * @event add
21178          * Fires when Records have been added to the Store
21179          * @param {Store} this
21180          * @param {Roo.data.Record[]} records The array of Records added
21181          * @param {Number} index The index at which the record(s) were added
21182          */
21183         add : true,
21184         /**
21185          * @event remove
21186          * Fires when a Record has been removed from the Store
21187          * @param {Store} this
21188          * @param {Roo.data.Record} record The Record that was removed
21189          * @param {Number} index The index at which the record was removed
21190          */
21191         remove : true,
21192         /**
21193          * @event update
21194          * Fires when a Record has been updated
21195          * @param {Store} this
21196          * @param {Roo.data.Record} record The Record that was updated
21197          * @param {String} operation The update operation being performed.  Value may be one of:
21198          * <pre><code>
21199  Roo.data.Record.EDIT
21200  Roo.data.Record.REJECT
21201  Roo.data.Record.COMMIT
21202          * </code></pre>
21203          */
21204         update : true,
21205         /**
21206          * @event clear
21207          * Fires when the data cache has been cleared.
21208          * @param {Store} this
21209          */
21210         clear : true,
21211         /**
21212          * @event beforeload
21213          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21214          * the load action will be canceled.
21215          * @param {Store} this
21216          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21217          */
21218         beforeload : true,
21219         /**
21220          * @event beforeloadadd
21221          * Fires after a new set of Records has been loaded.
21222          * @param {Store} this
21223          * @param {Roo.data.Record[]} records The Records that were loaded
21224          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21225          */
21226         beforeloadadd : true,
21227         /**
21228          * @event load
21229          * Fires after a new set of Records has been loaded, before they are added to the store.
21230          * @param {Store} this
21231          * @param {Roo.data.Record[]} records The Records that were loaded
21232          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21233          * @params {Object} return from reader
21234          */
21235         load : true,
21236         /**
21237          * @event loadexception
21238          * Fires if an exception occurs in the Proxy during loading.
21239          * Called with the signature of the Proxy's "loadexception" event.
21240          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21241          * 
21242          * @param {Proxy} 
21243          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21244          * @param {Object} load options 
21245          * @param {Object} jsonData from your request (normally this contains the Exception)
21246          */
21247         loadexception : true
21248     });
21249     
21250     if(this.proxy){
21251         this.proxy = Roo.factory(this.proxy, Roo.data);
21252         this.proxy.xmodule = this.xmodule || false;
21253         this.relayEvents(this.proxy,  ["loadexception"]);
21254     }
21255     this.sortToggle = {};
21256     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21257
21258     Roo.data.Store.superclass.constructor.call(this);
21259
21260     if(this.inlineData){
21261         this.loadData(this.inlineData);
21262         delete this.inlineData;
21263     }
21264 };
21265
21266 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21267      /**
21268     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21269     * without a remote query - used by combo/forms at present.
21270     */
21271     
21272     /**
21273     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21274     */
21275     /**
21276     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21277     */
21278     /**
21279     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21280     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21281     */
21282     /**
21283     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21284     * on any HTTP request
21285     */
21286     /**
21287     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21288     */
21289     /**
21290     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21291     */
21292     multiSort: false,
21293     /**
21294     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21295     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21296     */
21297     remoteSort : false,
21298
21299     /**
21300     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21301      * loaded or when a record is removed. (defaults to false).
21302     */
21303     pruneModifiedRecords : false,
21304
21305     // private
21306     lastOptions : null,
21307
21308     /**
21309      * Add Records to the Store and fires the add event.
21310      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21311      */
21312     add : function(records){
21313         records = [].concat(records);
21314         for(var i = 0, len = records.length; i < len; i++){
21315             records[i].join(this);
21316         }
21317         var index = this.data.length;
21318         this.data.addAll(records);
21319         this.fireEvent("add", this, records, index);
21320     },
21321
21322     /**
21323      * Remove a Record from the Store and fires the remove event.
21324      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21325      */
21326     remove : function(record){
21327         var index = this.data.indexOf(record);
21328         this.data.removeAt(index);
21329         if(this.pruneModifiedRecords){
21330             this.modified.remove(record);
21331         }
21332         this.fireEvent("remove", this, record, index);
21333     },
21334
21335     /**
21336      * Remove all Records from the Store and fires the clear event.
21337      */
21338     removeAll : function(){
21339         this.data.clear();
21340         if(this.pruneModifiedRecords){
21341             this.modified = [];
21342         }
21343         this.fireEvent("clear", this);
21344     },
21345
21346     /**
21347      * Inserts Records to the Store at the given index and fires the add event.
21348      * @param {Number} index The start index at which to insert the passed Records.
21349      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21350      */
21351     insert : function(index, records){
21352         records = [].concat(records);
21353         for(var i = 0, len = records.length; i < len; i++){
21354             this.data.insert(index, records[i]);
21355             records[i].join(this);
21356         }
21357         this.fireEvent("add", this, records, index);
21358     },
21359
21360     /**
21361      * Get the index within the cache of the passed Record.
21362      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21363      * @return {Number} The index of the passed Record. Returns -1 if not found.
21364      */
21365     indexOf : function(record){
21366         return this.data.indexOf(record);
21367     },
21368
21369     /**
21370      * Get the index within the cache of the Record with the passed id.
21371      * @param {String} id The id of the Record to find.
21372      * @return {Number} The index of the Record. Returns -1 if not found.
21373      */
21374     indexOfId : function(id){
21375         return this.data.indexOfKey(id);
21376     },
21377
21378     /**
21379      * Get the Record with the specified id.
21380      * @param {String} id The id of the Record to find.
21381      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21382      */
21383     getById : function(id){
21384         return this.data.key(id);
21385     },
21386
21387     /**
21388      * Get the Record at the specified index.
21389      * @param {Number} index The index of the Record to find.
21390      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21391      */
21392     getAt : function(index){
21393         return this.data.itemAt(index);
21394     },
21395
21396     /**
21397      * Returns a range of Records between specified indices.
21398      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21399      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21400      * @return {Roo.data.Record[]} An array of Records
21401      */
21402     getRange : function(start, end){
21403         return this.data.getRange(start, end);
21404     },
21405
21406     // private
21407     storeOptions : function(o){
21408         o = Roo.apply({}, o);
21409         delete o.callback;
21410         delete o.scope;
21411         this.lastOptions = o;
21412     },
21413
21414     /**
21415      * Loads the Record cache from the configured Proxy using the configured Reader.
21416      * <p>
21417      * If using remote paging, then the first load call must specify the <em>start</em>
21418      * and <em>limit</em> properties in the options.params property to establish the initial
21419      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21420      * <p>
21421      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21422      * and this call will return before the new data has been loaded. Perform any post-processing
21423      * in a callback function, or in a "load" event handler.</strong>
21424      * <p>
21425      * @param {Object} options An object containing properties which control loading options:<ul>
21426      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21427      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21428      * passed the following arguments:<ul>
21429      * <li>r : Roo.data.Record[]</li>
21430      * <li>options: Options object from the load call</li>
21431      * <li>success: Boolean success indicator</li></ul></li>
21432      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21433      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21434      * </ul>
21435      */
21436     load : function(options){
21437         options = options || {};
21438         if(this.fireEvent("beforeload", this, options) !== false){
21439             this.storeOptions(options);
21440             var p = Roo.apply(options.params || {}, this.baseParams);
21441             // if meta was not loaded from remote source.. try requesting it.
21442             if (!this.reader.metaFromRemote) {
21443                 p._requestMeta = 1;
21444             }
21445             if(this.sortInfo && this.remoteSort){
21446                 var pn = this.paramNames;
21447                 p[pn["sort"]] = this.sortInfo.field;
21448                 p[pn["dir"]] = this.sortInfo.direction;
21449             }
21450             if (this.multiSort) {
21451                 var pn = this.paramNames;
21452                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21453             }
21454             
21455             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21456         }
21457     },
21458
21459     /**
21460      * Reloads the Record cache from the configured Proxy using the configured Reader and
21461      * the options from the last load operation performed.
21462      * @param {Object} options (optional) An object containing properties which may override the options
21463      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21464      * the most recently used options are reused).
21465      */
21466     reload : function(options){
21467         this.load(Roo.applyIf(options||{}, this.lastOptions));
21468     },
21469
21470     // private
21471     // Called as a callback by the Reader during a load operation.
21472     loadRecords : function(o, options, success){
21473         if(!o || success === false){
21474             if(success !== false){
21475                 this.fireEvent("load", this, [], options, o);
21476             }
21477             if(options.callback){
21478                 options.callback.call(options.scope || this, [], options, false);
21479             }
21480             return;
21481         }
21482         // if data returned failure - throw an exception.
21483         if (o.success === false) {
21484             // show a message if no listener is registered.
21485             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21486                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21487             }
21488             // loadmask wil be hooked into this..
21489             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21490             return;
21491         }
21492         var r = o.records, t = o.totalRecords || r.length;
21493         
21494         this.fireEvent("beforeloadadd", this, r, options, o);
21495         
21496         if(!options || options.add !== true){
21497             if(this.pruneModifiedRecords){
21498                 this.modified = [];
21499             }
21500             for(var i = 0, len = r.length; i < len; i++){
21501                 r[i].join(this);
21502             }
21503             if(this.snapshot){
21504                 this.data = this.snapshot;
21505                 delete this.snapshot;
21506             }
21507             this.data.clear();
21508             this.data.addAll(r);
21509             this.totalLength = t;
21510             this.applySort();
21511             this.fireEvent("datachanged", this);
21512         }else{
21513             this.totalLength = Math.max(t, this.data.length+r.length);
21514             this.add(r);
21515         }
21516         this.fireEvent("load", this, r, options, o);
21517         if(options.callback){
21518             options.callback.call(options.scope || this, r, options, true);
21519         }
21520     },
21521
21522
21523     /**
21524      * Loads data from a passed data block. A Reader which understands the format of the data
21525      * must have been configured in the constructor.
21526      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21527      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21528      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21529      */
21530     loadData : function(o, append){
21531         var r = this.reader.readRecords(o);
21532         this.loadRecords(r, {add: append}, true);
21533     },
21534
21535     /**
21536      * Gets the number of cached records.
21537      * <p>
21538      * <em>If using paging, this may not be the total size of the dataset. If the data object
21539      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21540      * the data set size</em>
21541      */
21542     getCount : function(){
21543         return this.data.length || 0;
21544     },
21545
21546     /**
21547      * Gets the total number of records in the dataset as returned by the server.
21548      * <p>
21549      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21550      * the dataset size</em>
21551      */
21552     getTotalCount : function(){
21553         return this.totalLength || 0;
21554     },
21555
21556     /**
21557      * Returns the sort state of the Store as an object with two properties:
21558      * <pre><code>
21559  field {String} The name of the field by which the Records are sorted
21560  direction {String} The sort order, "ASC" or "DESC"
21561      * </code></pre>
21562      */
21563     getSortState : function(){
21564         return this.sortInfo;
21565     },
21566
21567     // private
21568     applySort : function(){
21569         if(this.sortInfo && !this.remoteSort){
21570             var s = this.sortInfo, f = s.field;
21571             var st = this.fields.get(f).sortType;
21572             var fn = function(r1, r2){
21573                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21574                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21575             };
21576             this.data.sort(s.direction, fn);
21577             if(this.snapshot && this.snapshot != this.data){
21578                 this.snapshot.sort(s.direction, fn);
21579             }
21580         }
21581     },
21582
21583     /**
21584      * Sets the default sort column and order to be used by the next load operation.
21585      * @param {String} fieldName The name of the field to sort by.
21586      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21587      */
21588     setDefaultSort : function(field, dir){
21589         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21590     },
21591
21592     /**
21593      * Sort the Records.
21594      * If remote sorting is used, the sort is performed on the server, and the cache is
21595      * reloaded. If local sorting is used, the cache is sorted internally.
21596      * @param {String} fieldName The name of the field to sort by.
21597      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21598      */
21599     sort : function(fieldName, dir){
21600         var f = this.fields.get(fieldName);
21601         if(!dir){
21602             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21603             
21604             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21605                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21606             }else{
21607                 dir = f.sortDir;
21608             }
21609         }
21610         this.sortToggle[f.name] = dir;
21611         this.sortInfo = {field: f.name, direction: dir};
21612         if(!this.remoteSort){
21613             this.applySort();
21614             this.fireEvent("datachanged", this);
21615         }else{
21616             this.load(this.lastOptions);
21617         }
21618     },
21619
21620     /**
21621      * Calls the specified function for each of the Records in the cache.
21622      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21623      * Returning <em>false</em> aborts and exits the iteration.
21624      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21625      */
21626     each : function(fn, scope){
21627         this.data.each(fn, scope);
21628     },
21629
21630     /**
21631      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21632      * (e.g., during paging).
21633      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21634      */
21635     getModifiedRecords : function(){
21636         return this.modified;
21637     },
21638
21639     // private
21640     createFilterFn : function(property, value, anyMatch){
21641         if(!value.exec){ // not a regex
21642             value = String(value);
21643             if(value.length == 0){
21644                 return false;
21645             }
21646             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21647         }
21648         return function(r){
21649             return value.test(r.data[property]);
21650         };
21651     },
21652
21653     /**
21654      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21655      * @param {String} property A field on your records
21656      * @param {Number} start The record index to start at (defaults to 0)
21657      * @param {Number} end The last record index to include (defaults to length - 1)
21658      * @return {Number} The sum
21659      */
21660     sum : function(property, start, end){
21661         var rs = this.data.items, v = 0;
21662         start = start || 0;
21663         end = (end || end === 0) ? end : rs.length-1;
21664
21665         for(var i = start; i <= end; i++){
21666             v += (rs[i].data[property] || 0);
21667         }
21668         return v;
21669     },
21670
21671     /**
21672      * Filter the records by a specified property.
21673      * @param {String} field A field on your records
21674      * @param {String/RegExp} value Either a string that the field
21675      * should start with or a RegExp to test against the field
21676      * @param {Boolean} anyMatch True to match any part not just the beginning
21677      */
21678     filter : function(property, value, anyMatch){
21679         var fn = this.createFilterFn(property, value, anyMatch);
21680         return fn ? this.filterBy(fn) : this.clearFilter();
21681     },
21682
21683     /**
21684      * Filter by a function. The specified function will be called with each
21685      * record in this data source. If the function returns true the record is included,
21686      * otherwise it is filtered.
21687      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21688      * @param {Object} scope (optional) The scope of the function (defaults to this)
21689      */
21690     filterBy : function(fn, scope){
21691         this.snapshot = this.snapshot || this.data;
21692         this.data = this.queryBy(fn, scope||this);
21693         this.fireEvent("datachanged", this);
21694     },
21695
21696     /**
21697      * Query the records by a specified property.
21698      * @param {String} field A field on your records
21699      * @param {String/RegExp} value Either a string that the field
21700      * should start with or a RegExp to test against the field
21701      * @param {Boolean} anyMatch True to match any part not just the beginning
21702      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21703      */
21704     query : function(property, value, anyMatch){
21705         var fn = this.createFilterFn(property, value, anyMatch);
21706         return fn ? this.queryBy(fn) : this.data.clone();
21707     },
21708
21709     /**
21710      * Query by a function. The specified function will be called with each
21711      * record in this data source. If the function returns true the record is included
21712      * in the results.
21713      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21714      * @param {Object} scope (optional) The scope of the function (defaults to this)
21715       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21716      **/
21717     queryBy : function(fn, scope){
21718         var data = this.snapshot || this.data;
21719         return data.filterBy(fn, scope||this);
21720     },
21721
21722     /**
21723      * Collects unique values for a particular dataIndex from this store.
21724      * @param {String} dataIndex The property to collect
21725      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21726      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21727      * @return {Array} An array of the unique values
21728      **/
21729     collect : function(dataIndex, allowNull, bypassFilter){
21730         var d = (bypassFilter === true && this.snapshot) ?
21731                 this.snapshot.items : this.data.items;
21732         var v, sv, r = [], l = {};
21733         for(var i = 0, len = d.length; i < len; i++){
21734             v = d[i].data[dataIndex];
21735             sv = String(v);
21736             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21737                 l[sv] = true;
21738                 r[r.length] = v;
21739             }
21740         }
21741         return r;
21742     },
21743
21744     /**
21745      * Revert to a view of the Record cache with no filtering applied.
21746      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21747      */
21748     clearFilter : function(suppressEvent){
21749         if(this.snapshot && this.snapshot != this.data){
21750             this.data = this.snapshot;
21751             delete this.snapshot;
21752             if(suppressEvent !== true){
21753                 this.fireEvent("datachanged", this);
21754             }
21755         }
21756     },
21757
21758     // private
21759     afterEdit : function(record){
21760         if(this.modified.indexOf(record) == -1){
21761             this.modified.push(record);
21762         }
21763         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21764     },
21765     
21766     // private
21767     afterReject : function(record){
21768         this.modified.remove(record);
21769         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21770     },
21771
21772     // private
21773     afterCommit : function(record){
21774         this.modified.remove(record);
21775         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21776     },
21777
21778     /**
21779      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21780      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21781      */
21782     commitChanges : function(){
21783         var m = this.modified.slice(0);
21784         this.modified = [];
21785         for(var i = 0, len = m.length; i < len; i++){
21786             m[i].commit();
21787         }
21788     },
21789
21790     /**
21791      * Cancel outstanding changes on all changed records.
21792      */
21793     rejectChanges : function(){
21794         var m = this.modified.slice(0);
21795         this.modified = [];
21796         for(var i = 0, len = m.length; i < len; i++){
21797             m[i].reject();
21798         }
21799     },
21800
21801     onMetaChange : function(meta, rtype, o){
21802         this.recordType = rtype;
21803         this.fields = rtype.prototype.fields;
21804         delete this.snapshot;
21805         this.sortInfo = meta.sortInfo || this.sortInfo;
21806         this.modified = [];
21807         this.fireEvent('metachange', this, this.reader.meta);
21808     },
21809     
21810     moveIndex : function(data, type)
21811     {
21812         var index = this.indexOf(data);
21813         
21814         var newIndex = index + type;
21815         
21816         this.remove(data);
21817         
21818         this.insert(newIndex, data);
21819         
21820     }
21821 });/*
21822  * Based on:
21823  * Ext JS Library 1.1.1
21824  * Copyright(c) 2006-2007, Ext JS, LLC.
21825  *
21826  * Originally Released Under LGPL - original licence link has changed is not relivant.
21827  *
21828  * Fork - LGPL
21829  * <script type="text/javascript">
21830  */
21831
21832 /**
21833  * @class Roo.data.SimpleStore
21834  * @extends Roo.data.Store
21835  * Small helper class to make creating Stores from Array data easier.
21836  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21837  * @cfg {Array} fields An array of field definition objects, or field name strings.
21838  * @cfg {Array} data The multi-dimensional array of data
21839  * @constructor
21840  * @param {Object} config
21841  */
21842 Roo.data.SimpleStore = function(config){
21843     Roo.data.SimpleStore.superclass.constructor.call(this, {
21844         isLocal : true,
21845         reader: new Roo.data.ArrayReader({
21846                 id: config.id
21847             },
21848             Roo.data.Record.create(config.fields)
21849         ),
21850         proxy : new Roo.data.MemoryProxy(config.data)
21851     });
21852     this.load();
21853 };
21854 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21855  * Based on:
21856  * Ext JS Library 1.1.1
21857  * Copyright(c) 2006-2007, Ext JS, LLC.
21858  *
21859  * Originally Released Under LGPL - original licence link has changed is not relivant.
21860  *
21861  * Fork - LGPL
21862  * <script type="text/javascript">
21863  */
21864
21865 /**
21866 /**
21867  * @extends Roo.data.Store
21868  * @class Roo.data.JsonStore
21869  * Small helper class to make creating Stores for JSON data easier. <br/>
21870 <pre><code>
21871 var store = new Roo.data.JsonStore({
21872     url: 'get-images.php',
21873     root: 'images',
21874     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21875 });
21876 </code></pre>
21877  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21878  * JsonReader and HttpProxy (unless inline data is provided).</b>
21879  * @cfg {Array} fields An array of field definition objects, or field name strings.
21880  * @constructor
21881  * @param {Object} config
21882  */
21883 Roo.data.JsonStore = function(c){
21884     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21885         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21886         reader: new Roo.data.JsonReader(c, c.fields)
21887     }));
21888 };
21889 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21890  * Based on:
21891  * Ext JS Library 1.1.1
21892  * Copyright(c) 2006-2007, Ext JS, LLC.
21893  *
21894  * Originally Released Under LGPL - original licence link has changed is not relivant.
21895  *
21896  * Fork - LGPL
21897  * <script type="text/javascript">
21898  */
21899
21900  
21901 Roo.data.Field = function(config){
21902     if(typeof config == "string"){
21903         config = {name: config};
21904     }
21905     Roo.apply(this, config);
21906     
21907     if(!this.type){
21908         this.type = "auto";
21909     }
21910     
21911     var st = Roo.data.SortTypes;
21912     // named sortTypes are supported, here we look them up
21913     if(typeof this.sortType == "string"){
21914         this.sortType = st[this.sortType];
21915     }
21916     
21917     // set default sortType for strings and dates
21918     if(!this.sortType){
21919         switch(this.type){
21920             case "string":
21921                 this.sortType = st.asUCString;
21922                 break;
21923             case "date":
21924                 this.sortType = st.asDate;
21925                 break;
21926             default:
21927                 this.sortType = st.none;
21928         }
21929     }
21930
21931     // define once
21932     var stripRe = /[\$,%]/g;
21933
21934     // prebuilt conversion function for this field, instead of
21935     // switching every time we're reading a value
21936     if(!this.convert){
21937         var cv, dateFormat = this.dateFormat;
21938         switch(this.type){
21939             case "":
21940             case "auto":
21941             case undefined:
21942                 cv = function(v){ return v; };
21943                 break;
21944             case "string":
21945                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21946                 break;
21947             case "int":
21948                 cv = function(v){
21949                     return v !== undefined && v !== null && v !== '' ?
21950                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21951                     };
21952                 break;
21953             case "float":
21954                 cv = function(v){
21955                     return v !== undefined && v !== null && v !== '' ?
21956                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21957                     };
21958                 break;
21959             case "bool":
21960             case "boolean":
21961                 cv = function(v){ return v === true || v === "true" || v == 1; };
21962                 break;
21963             case "date":
21964                 cv = function(v){
21965                     if(!v){
21966                         return '';
21967                     }
21968                     if(v instanceof Date){
21969                         return v;
21970                     }
21971                     if(dateFormat){
21972                         if(dateFormat == "timestamp"){
21973                             return new Date(v*1000);
21974                         }
21975                         return Date.parseDate(v, dateFormat);
21976                     }
21977                     var parsed = Date.parse(v);
21978                     return parsed ? new Date(parsed) : null;
21979                 };
21980              break;
21981             
21982         }
21983         this.convert = cv;
21984     }
21985 };
21986
21987 Roo.data.Field.prototype = {
21988     dateFormat: null,
21989     defaultValue: "",
21990     mapping: null,
21991     sortType : null,
21992     sortDir : "ASC"
21993 };/*
21994  * Based on:
21995  * Ext JS Library 1.1.1
21996  * Copyright(c) 2006-2007, Ext JS, LLC.
21997  *
21998  * Originally Released Under LGPL - original licence link has changed is not relivant.
21999  *
22000  * Fork - LGPL
22001  * <script type="text/javascript">
22002  */
22003  
22004 // Base class for reading structured data from a data source.  This class is intended to be
22005 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22006
22007 /**
22008  * @class Roo.data.DataReader
22009  * Base class for reading structured data from a data source.  This class is intended to be
22010  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22011  */
22012
22013 Roo.data.DataReader = function(meta, recordType){
22014     
22015     this.meta = meta;
22016     
22017     this.recordType = recordType instanceof Array ? 
22018         Roo.data.Record.create(recordType) : recordType;
22019 };
22020
22021 Roo.data.DataReader.prototype = {
22022      /**
22023      * Create an empty record
22024      * @param {Object} data (optional) - overlay some values
22025      * @return {Roo.data.Record} record created.
22026      */
22027     newRow :  function(d) {
22028         var da =  {};
22029         this.recordType.prototype.fields.each(function(c) {
22030             switch( c.type) {
22031                 case 'int' : da[c.name] = 0; break;
22032                 case 'date' : da[c.name] = new Date(); break;
22033                 case 'float' : da[c.name] = 0.0; break;
22034                 case 'boolean' : da[c.name] = false; break;
22035                 default : da[c.name] = ""; break;
22036             }
22037             
22038         });
22039         return new this.recordType(Roo.apply(da, d));
22040     }
22041     
22042 };/*
22043  * Based on:
22044  * Ext JS Library 1.1.1
22045  * Copyright(c) 2006-2007, Ext JS, LLC.
22046  *
22047  * Originally Released Under LGPL - original licence link has changed is not relivant.
22048  *
22049  * Fork - LGPL
22050  * <script type="text/javascript">
22051  */
22052
22053 /**
22054  * @class Roo.data.DataProxy
22055  * @extends Roo.data.Observable
22056  * This class is an abstract base class for implementations which provide retrieval of
22057  * unformatted data objects.<br>
22058  * <p>
22059  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22060  * (of the appropriate type which knows how to parse the data object) to provide a block of
22061  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22062  * <p>
22063  * Custom implementations must implement the load method as described in
22064  * {@link Roo.data.HttpProxy#load}.
22065  */
22066 Roo.data.DataProxy = function(){
22067     this.addEvents({
22068         /**
22069          * @event beforeload
22070          * Fires before a network request is made to retrieve a data object.
22071          * @param {Object} This DataProxy object.
22072          * @param {Object} params The params parameter to the load function.
22073          */
22074         beforeload : true,
22075         /**
22076          * @event load
22077          * Fires before the load method's callback is called.
22078          * @param {Object} This DataProxy object.
22079          * @param {Object} o The data object.
22080          * @param {Object} arg The callback argument object passed to the load function.
22081          */
22082         load : true,
22083         /**
22084          * @event loadexception
22085          * Fires if an Exception occurs during data retrieval.
22086          * @param {Object} This DataProxy object.
22087          * @param {Object} o The data object.
22088          * @param {Object} arg The callback argument object passed to the load function.
22089          * @param {Object} e The Exception.
22090          */
22091         loadexception : true
22092     });
22093     Roo.data.DataProxy.superclass.constructor.call(this);
22094 };
22095
22096 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22097
22098     /**
22099      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22100      */
22101 /*
22102  * Based on:
22103  * Ext JS Library 1.1.1
22104  * Copyright(c) 2006-2007, Ext JS, LLC.
22105  *
22106  * Originally Released Under LGPL - original licence link has changed is not relivant.
22107  *
22108  * Fork - LGPL
22109  * <script type="text/javascript">
22110  */
22111 /**
22112  * @class Roo.data.MemoryProxy
22113  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22114  * to the Reader when its load method is called.
22115  * @constructor
22116  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22117  */
22118 Roo.data.MemoryProxy = function(data){
22119     if (data.data) {
22120         data = data.data;
22121     }
22122     Roo.data.MemoryProxy.superclass.constructor.call(this);
22123     this.data = data;
22124 };
22125
22126 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22127     /**
22128      * Load data from the requested source (in this case an in-memory
22129      * data object passed to the constructor), read the data object into
22130      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22131      * process that block using the passed callback.
22132      * @param {Object} params This parameter is not used by the MemoryProxy class.
22133      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22134      * object into a block of Roo.data.Records.
22135      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22136      * The function must be passed <ul>
22137      * <li>The Record block object</li>
22138      * <li>The "arg" argument from the load function</li>
22139      * <li>A boolean success indicator</li>
22140      * </ul>
22141      * @param {Object} scope The scope in which to call the callback
22142      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22143      */
22144     load : function(params, reader, callback, scope, arg){
22145         params = params || {};
22146         var result;
22147         try {
22148             result = reader.readRecords(this.data);
22149         }catch(e){
22150             this.fireEvent("loadexception", this, arg, null, e);
22151             callback.call(scope, null, arg, false);
22152             return;
22153         }
22154         callback.call(scope, result, arg, true);
22155     },
22156     
22157     // private
22158     update : function(params, records){
22159         
22160     }
22161 });/*
22162  * Based on:
22163  * Ext JS Library 1.1.1
22164  * Copyright(c) 2006-2007, Ext JS, LLC.
22165  *
22166  * Originally Released Under LGPL - original licence link has changed is not relivant.
22167  *
22168  * Fork - LGPL
22169  * <script type="text/javascript">
22170  */
22171 /**
22172  * @class Roo.data.HttpProxy
22173  * @extends Roo.data.DataProxy
22174  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22175  * configured to reference a certain URL.<br><br>
22176  * <p>
22177  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22178  * from which the running page was served.<br><br>
22179  * <p>
22180  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22181  * <p>
22182  * Be aware that to enable the browser to parse an XML document, the server must set
22183  * the Content-Type header in the HTTP response to "text/xml".
22184  * @constructor
22185  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22186  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22187  * will be used to make the request.
22188  */
22189 Roo.data.HttpProxy = function(conn){
22190     Roo.data.HttpProxy.superclass.constructor.call(this);
22191     // is conn a conn config or a real conn?
22192     this.conn = conn;
22193     this.useAjax = !conn || !conn.events;
22194   
22195 };
22196
22197 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22198     // thse are take from connection...
22199     
22200     /**
22201      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22202      */
22203     /**
22204      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22205      * extra parameters to each request made by this object. (defaults to undefined)
22206      */
22207     /**
22208      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22209      *  to each request made by this object. (defaults to undefined)
22210      */
22211     /**
22212      * @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)
22213      */
22214     /**
22215      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22216      */
22217      /**
22218      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22219      * @type Boolean
22220      */
22221   
22222
22223     /**
22224      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22225      * @type Boolean
22226      */
22227     /**
22228      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22229      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22230      * a finer-grained basis than the DataProxy events.
22231      */
22232     getConnection : function(){
22233         return this.useAjax ? Roo.Ajax : this.conn;
22234     },
22235
22236     /**
22237      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22238      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22239      * process that block using the passed callback.
22240      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22241      * for the request to the remote server.
22242      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22243      * object into a block of Roo.data.Records.
22244      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22245      * The function must be passed <ul>
22246      * <li>The Record block object</li>
22247      * <li>The "arg" argument from the load function</li>
22248      * <li>A boolean success indicator</li>
22249      * </ul>
22250      * @param {Object} scope The scope in which to call the callback
22251      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22252      */
22253     load : function(params, reader, callback, scope, arg){
22254         if(this.fireEvent("beforeload", this, params) !== false){
22255             var  o = {
22256                 params : params || {},
22257                 request: {
22258                     callback : callback,
22259                     scope : scope,
22260                     arg : arg
22261                 },
22262                 reader: reader,
22263                 callback : this.loadResponse,
22264                 scope: this
22265             };
22266             if(this.useAjax){
22267                 Roo.applyIf(o, this.conn);
22268                 if(this.activeRequest){
22269                     Roo.Ajax.abort(this.activeRequest);
22270                 }
22271                 this.activeRequest = Roo.Ajax.request(o);
22272             }else{
22273                 this.conn.request(o);
22274             }
22275         }else{
22276             callback.call(scope||this, null, arg, false);
22277         }
22278     },
22279
22280     // private
22281     loadResponse : function(o, success, response){
22282         delete this.activeRequest;
22283         if(!success){
22284             this.fireEvent("loadexception", this, o, response);
22285             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22286             return;
22287         }
22288         var result;
22289         try {
22290             result = o.reader.read(response);
22291         }catch(e){
22292             this.fireEvent("loadexception", this, o, response, e);
22293             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22294             return;
22295         }
22296         
22297         this.fireEvent("load", this, o, o.request.arg);
22298         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22299     },
22300
22301     // private
22302     update : function(dataSet){
22303
22304     },
22305
22306     // private
22307     updateResponse : function(dataSet){
22308
22309     }
22310 });/*
22311  * Based on:
22312  * Ext JS Library 1.1.1
22313  * Copyright(c) 2006-2007, Ext JS, LLC.
22314  *
22315  * Originally Released Under LGPL - original licence link has changed is not relivant.
22316  *
22317  * Fork - LGPL
22318  * <script type="text/javascript">
22319  */
22320
22321 /**
22322  * @class Roo.data.ScriptTagProxy
22323  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22324  * other than the originating domain of the running page.<br><br>
22325  * <p>
22326  * <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
22327  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22328  * <p>
22329  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22330  * source code that is used as the source inside a &lt;script> tag.<br><br>
22331  * <p>
22332  * In order for the browser to process the returned data, the server must wrap the data object
22333  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22334  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22335  * depending on whether the callback name was passed:
22336  * <p>
22337  * <pre><code>
22338 boolean scriptTag = false;
22339 String cb = request.getParameter("callback");
22340 if (cb != null) {
22341     scriptTag = true;
22342     response.setContentType("text/javascript");
22343 } else {
22344     response.setContentType("application/x-json");
22345 }
22346 Writer out = response.getWriter();
22347 if (scriptTag) {
22348     out.write(cb + "(");
22349 }
22350 out.print(dataBlock.toJsonString());
22351 if (scriptTag) {
22352     out.write(");");
22353 }
22354 </pre></code>
22355  *
22356  * @constructor
22357  * @param {Object} config A configuration object.
22358  */
22359 Roo.data.ScriptTagProxy = function(config){
22360     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22361     Roo.apply(this, config);
22362     this.head = document.getElementsByTagName("head")[0];
22363 };
22364
22365 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22366
22367 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22368     /**
22369      * @cfg {String} url The URL from which to request the data object.
22370      */
22371     /**
22372      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22373      */
22374     timeout : 30000,
22375     /**
22376      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22377      * the server the name of the callback function set up by the load call to process the returned data object.
22378      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22379      * javascript output which calls this named function passing the data object as its only parameter.
22380      */
22381     callbackParam : "callback",
22382     /**
22383      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22384      * name to the request.
22385      */
22386     nocache : true,
22387
22388     /**
22389      * Load data from the configured URL, read the data object into
22390      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22391      * process that block using the passed callback.
22392      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22393      * for the request to the remote server.
22394      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22395      * object into a block of Roo.data.Records.
22396      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22397      * The function must be passed <ul>
22398      * <li>The Record block object</li>
22399      * <li>The "arg" argument from the load function</li>
22400      * <li>A boolean success indicator</li>
22401      * </ul>
22402      * @param {Object} scope The scope in which to call the callback
22403      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22404      */
22405     load : function(params, reader, callback, scope, arg){
22406         if(this.fireEvent("beforeload", this, params) !== false){
22407
22408             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22409
22410             var url = this.url;
22411             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22412             if(this.nocache){
22413                 url += "&_dc=" + (new Date().getTime());
22414             }
22415             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22416             var trans = {
22417                 id : transId,
22418                 cb : "stcCallback"+transId,
22419                 scriptId : "stcScript"+transId,
22420                 params : params,
22421                 arg : arg,
22422                 url : url,
22423                 callback : callback,
22424                 scope : scope,
22425                 reader : reader
22426             };
22427             var conn = this;
22428
22429             window[trans.cb] = function(o){
22430                 conn.handleResponse(o, trans);
22431             };
22432
22433             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22434
22435             if(this.autoAbort !== false){
22436                 this.abort();
22437             }
22438
22439             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22440
22441             var script = document.createElement("script");
22442             script.setAttribute("src", url);
22443             script.setAttribute("type", "text/javascript");
22444             script.setAttribute("id", trans.scriptId);
22445             this.head.appendChild(script);
22446
22447             this.trans = trans;
22448         }else{
22449             callback.call(scope||this, null, arg, false);
22450         }
22451     },
22452
22453     // private
22454     isLoading : function(){
22455         return this.trans ? true : false;
22456     },
22457
22458     /**
22459      * Abort the current server request.
22460      */
22461     abort : function(){
22462         if(this.isLoading()){
22463             this.destroyTrans(this.trans);
22464         }
22465     },
22466
22467     // private
22468     destroyTrans : function(trans, isLoaded){
22469         this.head.removeChild(document.getElementById(trans.scriptId));
22470         clearTimeout(trans.timeoutId);
22471         if(isLoaded){
22472             window[trans.cb] = undefined;
22473             try{
22474                 delete window[trans.cb];
22475             }catch(e){}
22476         }else{
22477             // if hasn't been loaded, wait for load to remove it to prevent script error
22478             window[trans.cb] = function(){
22479                 window[trans.cb] = undefined;
22480                 try{
22481                     delete window[trans.cb];
22482                 }catch(e){}
22483             };
22484         }
22485     },
22486
22487     // private
22488     handleResponse : function(o, trans){
22489         this.trans = false;
22490         this.destroyTrans(trans, true);
22491         var result;
22492         try {
22493             result = trans.reader.readRecords(o);
22494         }catch(e){
22495             this.fireEvent("loadexception", this, o, trans.arg, e);
22496             trans.callback.call(trans.scope||window, null, trans.arg, false);
22497             return;
22498         }
22499         this.fireEvent("load", this, o, trans.arg);
22500         trans.callback.call(trans.scope||window, result, trans.arg, true);
22501     },
22502
22503     // private
22504     handleFailure : function(trans){
22505         this.trans = false;
22506         this.destroyTrans(trans, false);
22507         this.fireEvent("loadexception", this, null, trans.arg);
22508         trans.callback.call(trans.scope||window, null, trans.arg, false);
22509     }
22510 });/*
22511  * Based on:
22512  * Ext JS Library 1.1.1
22513  * Copyright(c) 2006-2007, Ext JS, LLC.
22514  *
22515  * Originally Released Under LGPL - original licence link has changed is not relivant.
22516  *
22517  * Fork - LGPL
22518  * <script type="text/javascript">
22519  */
22520
22521 /**
22522  * @class Roo.data.JsonReader
22523  * @extends Roo.data.DataReader
22524  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22525  * based on mappings in a provided Roo.data.Record constructor.
22526  * 
22527  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22528  * in the reply previously. 
22529  * 
22530  * <p>
22531  * Example code:
22532  * <pre><code>
22533 var RecordDef = Roo.data.Record.create([
22534     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22535     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22536 ]);
22537 var myReader = new Roo.data.JsonReader({
22538     totalProperty: "results",    // The property which contains the total dataset size (optional)
22539     root: "rows",                // The property which contains an Array of row objects
22540     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22541 }, RecordDef);
22542 </code></pre>
22543  * <p>
22544  * This would consume a JSON file like this:
22545  * <pre><code>
22546 { 'results': 2, 'rows': [
22547     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22548     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22549 }
22550 </code></pre>
22551  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22552  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22553  * paged from the remote server.
22554  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22555  * @cfg {String} root name of the property which contains the Array of row objects.
22556  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22557  * @constructor
22558  * Create a new JsonReader
22559  * @param {Object} meta Metadata configuration options
22560  * @param {Object} recordType Either an Array of field definition objects,
22561  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22562  */
22563 Roo.data.JsonReader = function(meta, recordType){
22564     
22565     meta = meta || {};
22566     // set some defaults:
22567     Roo.applyIf(meta, {
22568         totalProperty: 'total',
22569         successProperty : 'success',
22570         root : 'data',
22571         id : 'id'
22572     });
22573     
22574     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22575 };
22576 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22577     
22578     /**
22579      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22580      * Used by Store query builder to append _requestMeta to params.
22581      * 
22582      */
22583     metaFromRemote : false,
22584     /**
22585      * This method is only used by a DataProxy which has retrieved data from a remote server.
22586      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22587      * @return {Object} data A data block which is used by an Roo.data.Store object as
22588      * a cache of Roo.data.Records.
22589      */
22590     read : function(response){
22591         var json = response.responseText;
22592        
22593         var o = /* eval:var:o */ eval("("+json+")");
22594         if(!o) {
22595             throw {message: "JsonReader.read: Json object not found"};
22596         }
22597         
22598         if(o.metaData){
22599             
22600             delete this.ef;
22601             this.metaFromRemote = true;
22602             this.meta = o.metaData;
22603             this.recordType = Roo.data.Record.create(o.metaData.fields);
22604             this.onMetaChange(this.meta, this.recordType, o);
22605         }
22606         return this.readRecords(o);
22607     },
22608
22609     // private function a store will implement
22610     onMetaChange : function(meta, recordType, o){
22611
22612     },
22613
22614     /**
22615          * @ignore
22616          */
22617     simpleAccess: function(obj, subsc) {
22618         return obj[subsc];
22619     },
22620
22621         /**
22622          * @ignore
22623          */
22624     getJsonAccessor: function(){
22625         var re = /[\[\.]/;
22626         return function(expr) {
22627             try {
22628                 return(re.test(expr))
22629                     ? new Function("obj", "return obj." + expr)
22630                     : function(obj){
22631                         return obj[expr];
22632                     };
22633             } catch(e){}
22634             return Roo.emptyFn;
22635         };
22636     }(),
22637
22638     /**
22639      * Create a data block containing Roo.data.Records from an XML document.
22640      * @param {Object} o An object which contains an Array of row objects in the property specified
22641      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22642      * which contains the total size of the dataset.
22643      * @return {Object} data A data block which is used by an Roo.data.Store object as
22644      * a cache of Roo.data.Records.
22645      */
22646     readRecords : function(o){
22647         /**
22648          * After any data loads, the raw JSON data is available for further custom processing.
22649          * @type Object
22650          */
22651         this.o = o;
22652         var s = this.meta, Record = this.recordType,
22653             f = Record.prototype.fields, fi = f.items, fl = f.length;
22654
22655 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22656         if (!this.ef) {
22657             if(s.totalProperty) {
22658                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22659                 }
22660                 if(s.successProperty) {
22661                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22662                 }
22663                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22664                 if (s.id) {
22665                         var g = this.getJsonAccessor(s.id);
22666                         this.getId = function(rec) {
22667                                 var r = g(rec);
22668                                 return (r === undefined || r === "") ? null : r;
22669                         };
22670                 } else {
22671                         this.getId = function(){return null;};
22672                 }
22673             this.ef = [];
22674             for(var jj = 0; jj < fl; jj++){
22675                 f = fi[jj];
22676                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22677                 this.ef[jj] = this.getJsonAccessor(map);
22678             }
22679         }
22680
22681         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22682         if(s.totalProperty){
22683             var vt = parseInt(this.getTotal(o), 10);
22684             if(!isNaN(vt)){
22685                 totalRecords = vt;
22686             }
22687         }
22688         if(s.successProperty){
22689             var vs = this.getSuccess(o);
22690             if(vs === false || vs === 'false'){
22691                 success = false;
22692             }
22693         }
22694         var records = [];
22695             for(var i = 0; i < c; i++){
22696                     var n = root[i];
22697                 var values = {};
22698                 var id = this.getId(n);
22699                 for(var j = 0; j < fl; j++){
22700                     f = fi[j];
22701                 var v = this.ef[j](n);
22702                 if (!f.convert) {
22703                     Roo.log('missing convert for ' + f.name);
22704                     Roo.log(f);
22705                     continue;
22706                 }
22707                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22708                 }
22709                 var record = new Record(values, id);
22710                 record.json = n;
22711                 records[i] = record;
22712             }
22713             return {
22714             raw : o,
22715                 success : success,
22716                 records : records,
22717                 totalRecords : totalRecords
22718             };
22719     }
22720 });/*
22721  * Based on:
22722  * Ext JS Library 1.1.1
22723  * Copyright(c) 2006-2007, Ext JS, LLC.
22724  *
22725  * Originally Released Under LGPL - original licence link has changed is not relivant.
22726  *
22727  * Fork - LGPL
22728  * <script type="text/javascript">
22729  */
22730
22731 /**
22732  * @class Roo.data.XmlReader
22733  * @extends Roo.data.DataReader
22734  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22735  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22736  * <p>
22737  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22738  * header in the HTTP response must be set to "text/xml".</em>
22739  * <p>
22740  * Example code:
22741  * <pre><code>
22742 var RecordDef = Roo.data.Record.create([
22743    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22744    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22745 ]);
22746 var myReader = new Roo.data.XmlReader({
22747    totalRecords: "results", // The element which contains the total dataset size (optional)
22748    record: "row",           // The repeated element which contains row information
22749    id: "id"                 // The element within the row that provides an ID for the record (optional)
22750 }, RecordDef);
22751 </code></pre>
22752  * <p>
22753  * This would consume an XML file like this:
22754  * <pre><code>
22755 &lt;?xml?>
22756 &lt;dataset>
22757  &lt;results>2&lt;/results>
22758  &lt;row>
22759    &lt;id>1&lt;/id>
22760    &lt;name>Bill&lt;/name>
22761    &lt;occupation>Gardener&lt;/occupation>
22762  &lt;/row>
22763  &lt;row>
22764    &lt;id>2&lt;/id>
22765    &lt;name>Ben&lt;/name>
22766    &lt;occupation>Horticulturalist&lt;/occupation>
22767  &lt;/row>
22768 &lt;/dataset>
22769 </code></pre>
22770  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22771  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22772  * paged from the remote server.
22773  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22774  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22775  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22776  * a record identifier value.
22777  * @constructor
22778  * Create a new XmlReader
22779  * @param {Object} meta Metadata configuration options
22780  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22781  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22782  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22783  */
22784 Roo.data.XmlReader = function(meta, recordType){
22785     meta = meta || {};
22786     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22787 };
22788 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22789     /**
22790      * This method is only used by a DataProxy which has retrieved data from a remote server.
22791          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22792          * to contain a method called 'responseXML' that returns an XML document object.
22793      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22794      * a cache of Roo.data.Records.
22795      */
22796     read : function(response){
22797         var doc = response.responseXML;
22798         if(!doc) {
22799             throw {message: "XmlReader.read: XML Document not available"};
22800         }
22801         return this.readRecords(doc);
22802     },
22803
22804     /**
22805      * Create a data block containing Roo.data.Records from an XML document.
22806          * @param {Object} doc A parsed XML document.
22807      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22808      * a cache of Roo.data.Records.
22809      */
22810     readRecords : function(doc){
22811         /**
22812          * After any data loads/reads, the raw XML Document is available for further custom processing.
22813          * @type XMLDocument
22814          */
22815         this.xmlData = doc;
22816         var root = doc.documentElement || doc;
22817         var q = Roo.DomQuery;
22818         var recordType = this.recordType, fields = recordType.prototype.fields;
22819         var sid = this.meta.id;
22820         var totalRecords = 0, success = true;
22821         if(this.meta.totalRecords){
22822             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22823         }
22824         
22825         if(this.meta.success){
22826             var sv = q.selectValue(this.meta.success, root, true);
22827             success = sv !== false && sv !== 'false';
22828         }
22829         var records = [];
22830         var ns = q.select(this.meta.record, root);
22831         for(var i = 0, len = ns.length; i < len; i++) {
22832                 var n = ns[i];
22833                 var values = {};
22834                 var id = sid ? q.selectValue(sid, n) : undefined;
22835                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22836                     var f = fields.items[j];
22837                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22838                     v = f.convert(v);
22839                     values[f.name] = v;
22840                 }
22841                 var record = new recordType(values, id);
22842                 record.node = n;
22843                 records[records.length] = record;
22844             }
22845
22846             return {
22847                 success : success,
22848                 records : records,
22849                 totalRecords : totalRecords || records.length
22850             };
22851     }
22852 });/*
22853  * Based on:
22854  * Ext JS Library 1.1.1
22855  * Copyright(c) 2006-2007, Ext JS, LLC.
22856  *
22857  * Originally Released Under LGPL - original licence link has changed is not relivant.
22858  *
22859  * Fork - LGPL
22860  * <script type="text/javascript">
22861  */
22862
22863 /**
22864  * @class Roo.data.ArrayReader
22865  * @extends Roo.data.DataReader
22866  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22867  * Each element of that Array represents a row of data fields. The
22868  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22869  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22870  * <p>
22871  * Example code:.
22872  * <pre><code>
22873 var RecordDef = Roo.data.Record.create([
22874     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22875     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22876 ]);
22877 var myReader = new Roo.data.ArrayReader({
22878     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22879 }, RecordDef);
22880 </code></pre>
22881  * <p>
22882  * This would consume an Array like this:
22883  * <pre><code>
22884 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22885   </code></pre>
22886  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22887  * @constructor
22888  * Create a new JsonReader
22889  * @param {Object} meta Metadata configuration options.
22890  * @param {Object} recordType Either an Array of field definition objects
22891  * as specified to {@link Roo.data.Record#create},
22892  * or an {@link Roo.data.Record} object
22893  * created using {@link Roo.data.Record#create}.
22894  */
22895 Roo.data.ArrayReader = function(meta, recordType){
22896     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22897 };
22898
22899 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22900     /**
22901      * Create a data block containing Roo.data.Records from an XML document.
22902      * @param {Object} o An Array of row objects which represents the dataset.
22903      * @return {Object} data A data block which is used by an Roo.data.Store object as
22904      * a cache of Roo.data.Records.
22905      */
22906     readRecords : function(o){
22907         var sid = this.meta ? this.meta.id : null;
22908         var recordType = this.recordType, fields = recordType.prototype.fields;
22909         var records = [];
22910         var root = o;
22911             for(var i = 0; i < root.length; i++){
22912                     var n = root[i];
22913                 var values = {};
22914                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22915                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22916                 var f = fields.items[j];
22917                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22918                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22919                 v = f.convert(v);
22920                 values[f.name] = v;
22921             }
22922                 var record = new recordType(values, id);
22923                 record.json = n;
22924                 records[records.length] = record;
22925             }
22926             return {
22927                 records : records,
22928                 totalRecords : records.length
22929             };
22930     }
22931 });/*
22932  * Based on:
22933  * Ext JS Library 1.1.1
22934  * Copyright(c) 2006-2007, Ext JS, LLC.
22935  *
22936  * Originally Released Under LGPL - original licence link has changed is not relivant.
22937  *
22938  * Fork - LGPL
22939  * <script type="text/javascript">
22940  */
22941
22942
22943 /**
22944  * @class Roo.data.Tree
22945  * @extends Roo.util.Observable
22946  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22947  * in the tree have most standard DOM functionality.
22948  * @constructor
22949  * @param {Node} root (optional) The root node
22950  */
22951 Roo.data.Tree = function(root){
22952    this.nodeHash = {};
22953    /**
22954     * The root node for this tree
22955     * @type Node
22956     */
22957    this.root = null;
22958    if(root){
22959        this.setRootNode(root);
22960    }
22961    this.addEvents({
22962        /**
22963         * @event append
22964         * Fires when a new child node is appended to a node in this tree.
22965         * @param {Tree} tree The owner tree
22966         * @param {Node} parent The parent node
22967         * @param {Node} node The newly appended node
22968         * @param {Number} index The index of the newly appended node
22969         */
22970        "append" : true,
22971        /**
22972         * @event remove
22973         * Fires when a child node is removed from a node in this tree.
22974         * @param {Tree} tree The owner tree
22975         * @param {Node} parent The parent node
22976         * @param {Node} node The child node removed
22977         */
22978        "remove" : true,
22979        /**
22980         * @event move
22981         * Fires when a node is moved to a new location in the tree
22982         * @param {Tree} tree The owner tree
22983         * @param {Node} node The node moved
22984         * @param {Node} oldParent The old parent of this node
22985         * @param {Node} newParent The new parent of this node
22986         * @param {Number} index The index it was moved to
22987         */
22988        "move" : true,
22989        /**
22990         * @event insert
22991         * Fires when a new child node is inserted in a node in this tree.
22992         * @param {Tree} tree The owner tree
22993         * @param {Node} parent The parent node
22994         * @param {Node} node The child node inserted
22995         * @param {Node} refNode The child node the node was inserted before
22996         */
22997        "insert" : true,
22998        /**
22999         * @event beforeappend
23000         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23001         * @param {Tree} tree The owner tree
23002         * @param {Node} parent The parent node
23003         * @param {Node} node The child node to be appended
23004         */
23005        "beforeappend" : true,
23006        /**
23007         * @event beforeremove
23008         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23009         * @param {Tree} tree The owner tree
23010         * @param {Node} parent The parent node
23011         * @param {Node} node The child node to be removed
23012         */
23013        "beforeremove" : true,
23014        /**
23015         * @event beforemove
23016         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23017         * @param {Tree} tree The owner tree
23018         * @param {Node} node The node being moved
23019         * @param {Node} oldParent The parent of the node
23020         * @param {Node} newParent The new parent the node is moving to
23021         * @param {Number} index The index it is being moved to
23022         */
23023        "beforemove" : true,
23024        /**
23025         * @event beforeinsert
23026         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23027         * @param {Tree} tree The owner tree
23028         * @param {Node} parent The parent node
23029         * @param {Node} node The child node to be inserted
23030         * @param {Node} refNode The child node the node is being inserted before
23031         */
23032        "beforeinsert" : true
23033    });
23034
23035     Roo.data.Tree.superclass.constructor.call(this);
23036 };
23037
23038 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23039     pathSeparator: "/",
23040
23041     proxyNodeEvent : function(){
23042         return this.fireEvent.apply(this, arguments);
23043     },
23044
23045     /**
23046      * Returns the root node for this tree.
23047      * @return {Node}
23048      */
23049     getRootNode : function(){
23050         return this.root;
23051     },
23052
23053     /**
23054      * Sets the root node for this tree.
23055      * @param {Node} node
23056      * @return {Node}
23057      */
23058     setRootNode : function(node){
23059         this.root = node;
23060         node.ownerTree = this;
23061         node.isRoot = true;
23062         this.registerNode(node);
23063         return node;
23064     },
23065
23066     /**
23067      * Gets a node in this tree by its id.
23068      * @param {String} id
23069      * @return {Node}
23070      */
23071     getNodeById : function(id){
23072         return this.nodeHash[id];
23073     },
23074
23075     registerNode : function(node){
23076         this.nodeHash[node.id] = node;
23077     },
23078
23079     unregisterNode : function(node){
23080         delete this.nodeHash[node.id];
23081     },
23082
23083     toString : function(){
23084         return "[Tree"+(this.id?" "+this.id:"")+"]";
23085     }
23086 });
23087
23088 /**
23089  * @class Roo.data.Node
23090  * @extends Roo.util.Observable
23091  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23092  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23093  * @constructor
23094  * @param {Object} attributes The attributes/config for the node
23095  */
23096 Roo.data.Node = function(attributes){
23097     /**
23098      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23099      * @type {Object}
23100      */
23101     this.attributes = attributes || {};
23102     this.leaf = this.attributes.leaf;
23103     /**
23104      * The node id. @type String
23105      */
23106     this.id = this.attributes.id;
23107     if(!this.id){
23108         this.id = Roo.id(null, "ynode-");
23109         this.attributes.id = this.id;
23110     }
23111      
23112     
23113     /**
23114      * All child nodes of this node. @type Array
23115      */
23116     this.childNodes = [];
23117     if(!this.childNodes.indexOf){ // indexOf is a must
23118         this.childNodes.indexOf = function(o){
23119             for(var i = 0, len = this.length; i < len; i++){
23120                 if(this[i] == o) {
23121                     return i;
23122                 }
23123             }
23124             return -1;
23125         };
23126     }
23127     /**
23128      * The parent node for this node. @type Node
23129      */
23130     this.parentNode = null;
23131     /**
23132      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23133      */
23134     this.firstChild = null;
23135     /**
23136      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23137      */
23138     this.lastChild = null;
23139     /**
23140      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23141      */
23142     this.previousSibling = null;
23143     /**
23144      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23145      */
23146     this.nextSibling = null;
23147
23148     this.addEvents({
23149        /**
23150         * @event append
23151         * Fires when a new child node is appended
23152         * @param {Tree} tree The owner tree
23153         * @param {Node} this This node
23154         * @param {Node} node The newly appended node
23155         * @param {Number} index The index of the newly appended node
23156         */
23157        "append" : true,
23158        /**
23159         * @event remove
23160         * Fires when a child node is removed
23161         * @param {Tree} tree The owner tree
23162         * @param {Node} this This node
23163         * @param {Node} node The removed node
23164         */
23165        "remove" : true,
23166        /**
23167         * @event move
23168         * Fires when this node is moved to a new location in the tree
23169         * @param {Tree} tree The owner tree
23170         * @param {Node} this This node
23171         * @param {Node} oldParent The old parent of this node
23172         * @param {Node} newParent The new parent of this node
23173         * @param {Number} index The index it was moved to
23174         */
23175        "move" : true,
23176        /**
23177         * @event insert
23178         * Fires when a new child node is inserted.
23179         * @param {Tree} tree The owner tree
23180         * @param {Node} this This node
23181         * @param {Node} node The child node inserted
23182         * @param {Node} refNode The child node the node was inserted before
23183         */
23184        "insert" : true,
23185        /**
23186         * @event beforeappend
23187         * Fires before a new child is appended, return false to cancel the append.
23188         * @param {Tree} tree The owner tree
23189         * @param {Node} this This node
23190         * @param {Node} node The child node to be appended
23191         */
23192        "beforeappend" : true,
23193        /**
23194         * @event beforeremove
23195         * Fires before a child is removed, return false to cancel the remove.
23196         * @param {Tree} tree The owner tree
23197         * @param {Node} this This node
23198         * @param {Node} node The child node to be removed
23199         */
23200        "beforeremove" : true,
23201        /**
23202         * @event beforemove
23203         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23204         * @param {Tree} tree The owner tree
23205         * @param {Node} this This node
23206         * @param {Node} oldParent The parent of this node
23207         * @param {Node} newParent The new parent this node is moving to
23208         * @param {Number} index The index it is being moved to
23209         */
23210        "beforemove" : true,
23211        /**
23212         * @event beforeinsert
23213         * Fires before a new child is inserted, return false to cancel the insert.
23214         * @param {Tree} tree The owner tree
23215         * @param {Node} this This node
23216         * @param {Node} node The child node to be inserted
23217         * @param {Node} refNode The child node the node is being inserted before
23218         */
23219        "beforeinsert" : true
23220    });
23221     this.listeners = this.attributes.listeners;
23222     Roo.data.Node.superclass.constructor.call(this);
23223 };
23224
23225 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23226     fireEvent : function(evtName){
23227         // first do standard event for this node
23228         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23229             return false;
23230         }
23231         // then bubble it up to the tree if the event wasn't cancelled
23232         var ot = this.getOwnerTree();
23233         if(ot){
23234             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23235                 return false;
23236             }
23237         }
23238         return true;
23239     },
23240
23241     /**
23242      * Returns true if this node is a leaf
23243      * @return {Boolean}
23244      */
23245     isLeaf : function(){
23246         return this.leaf === true;
23247     },
23248
23249     // private
23250     setFirstChild : function(node){
23251         this.firstChild = node;
23252     },
23253
23254     //private
23255     setLastChild : function(node){
23256         this.lastChild = node;
23257     },
23258
23259
23260     /**
23261      * Returns true if this node is the last child of its parent
23262      * @return {Boolean}
23263      */
23264     isLast : function(){
23265        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23266     },
23267
23268     /**
23269      * Returns true if this node is the first child of its parent
23270      * @return {Boolean}
23271      */
23272     isFirst : function(){
23273        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23274     },
23275
23276     hasChildNodes : function(){
23277         return !this.isLeaf() && this.childNodes.length > 0;
23278     },
23279
23280     /**
23281      * Insert node(s) as the last child node of this node.
23282      * @param {Node/Array} node The node or Array of nodes to append
23283      * @return {Node} The appended node if single append, or null if an array was passed
23284      */
23285     appendChild : function(node){
23286         var multi = false;
23287         if(node instanceof Array){
23288             multi = node;
23289         }else if(arguments.length > 1){
23290             multi = arguments;
23291         }
23292         // if passed an array or multiple args do them one by one
23293         if(multi){
23294             for(var i = 0, len = multi.length; i < len; i++) {
23295                 this.appendChild(multi[i]);
23296             }
23297         }else{
23298             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23299                 return false;
23300             }
23301             var index = this.childNodes.length;
23302             var oldParent = node.parentNode;
23303             // it's a move, make sure we move it cleanly
23304             if(oldParent){
23305                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23306                     return false;
23307                 }
23308                 oldParent.removeChild(node);
23309             }
23310             index = this.childNodes.length;
23311             if(index == 0){
23312                 this.setFirstChild(node);
23313             }
23314             this.childNodes.push(node);
23315             node.parentNode = this;
23316             var ps = this.childNodes[index-1];
23317             if(ps){
23318                 node.previousSibling = ps;
23319                 ps.nextSibling = node;
23320             }else{
23321                 node.previousSibling = null;
23322             }
23323             node.nextSibling = null;
23324             this.setLastChild(node);
23325             node.setOwnerTree(this.getOwnerTree());
23326             this.fireEvent("append", this.ownerTree, this, node, index);
23327             if(oldParent){
23328                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23329             }
23330             return node;
23331         }
23332     },
23333
23334     /**
23335      * Removes a child node from this node.
23336      * @param {Node} node The node to remove
23337      * @return {Node} The removed node
23338      */
23339     removeChild : function(node){
23340         var index = this.childNodes.indexOf(node);
23341         if(index == -1){
23342             return false;
23343         }
23344         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23345             return false;
23346         }
23347
23348         // remove it from childNodes collection
23349         this.childNodes.splice(index, 1);
23350
23351         // update siblings
23352         if(node.previousSibling){
23353             node.previousSibling.nextSibling = node.nextSibling;
23354         }
23355         if(node.nextSibling){
23356             node.nextSibling.previousSibling = node.previousSibling;
23357         }
23358
23359         // update child refs
23360         if(this.firstChild == node){
23361             this.setFirstChild(node.nextSibling);
23362         }
23363         if(this.lastChild == node){
23364             this.setLastChild(node.previousSibling);
23365         }
23366
23367         node.setOwnerTree(null);
23368         // clear any references from the node
23369         node.parentNode = null;
23370         node.previousSibling = null;
23371         node.nextSibling = null;
23372         this.fireEvent("remove", this.ownerTree, this, node);
23373         return node;
23374     },
23375
23376     /**
23377      * Inserts the first node before the second node in this nodes childNodes collection.
23378      * @param {Node} node The node to insert
23379      * @param {Node} refNode The node to insert before (if null the node is appended)
23380      * @return {Node} The inserted node
23381      */
23382     insertBefore : function(node, refNode){
23383         if(!refNode){ // like standard Dom, refNode can be null for append
23384             return this.appendChild(node);
23385         }
23386         // nothing to do
23387         if(node == refNode){
23388             return false;
23389         }
23390
23391         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23392             return false;
23393         }
23394         var index = this.childNodes.indexOf(refNode);
23395         var oldParent = node.parentNode;
23396         var refIndex = index;
23397
23398         // when moving internally, indexes will change after remove
23399         if(oldParent == this && this.childNodes.indexOf(node) < index){
23400             refIndex--;
23401         }
23402
23403         // it's a move, make sure we move it cleanly
23404         if(oldParent){
23405             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23406                 return false;
23407             }
23408             oldParent.removeChild(node);
23409         }
23410         if(refIndex == 0){
23411             this.setFirstChild(node);
23412         }
23413         this.childNodes.splice(refIndex, 0, node);
23414         node.parentNode = this;
23415         var ps = this.childNodes[refIndex-1];
23416         if(ps){
23417             node.previousSibling = ps;
23418             ps.nextSibling = node;
23419         }else{
23420             node.previousSibling = null;
23421         }
23422         node.nextSibling = refNode;
23423         refNode.previousSibling = node;
23424         node.setOwnerTree(this.getOwnerTree());
23425         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23426         if(oldParent){
23427             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23428         }
23429         return node;
23430     },
23431
23432     /**
23433      * Returns the child node at the specified index.
23434      * @param {Number} index
23435      * @return {Node}
23436      */
23437     item : function(index){
23438         return this.childNodes[index];
23439     },
23440
23441     /**
23442      * Replaces one child node in this node with another.
23443      * @param {Node} newChild The replacement node
23444      * @param {Node} oldChild The node to replace
23445      * @return {Node} The replaced node
23446      */
23447     replaceChild : function(newChild, oldChild){
23448         this.insertBefore(newChild, oldChild);
23449         this.removeChild(oldChild);
23450         return oldChild;
23451     },
23452
23453     /**
23454      * Returns the index of a child node
23455      * @param {Node} node
23456      * @return {Number} The index of the node or -1 if it was not found
23457      */
23458     indexOf : function(child){
23459         return this.childNodes.indexOf(child);
23460     },
23461
23462     /**
23463      * Returns the tree this node is in.
23464      * @return {Tree}
23465      */
23466     getOwnerTree : function(){
23467         // if it doesn't have one, look for one
23468         if(!this.ownerTree){
23469             var p = this;
23470             while(p){
23471                 if(p.ownerTree){
23472                     this.ownerTree = p.ownerTree;
23473                     break;
23474                 }
23475                 p = p.parentNode;
23476             }
23477         }
23478         return this.ownerTree;
23479     },
23480
23481     /**
23482      * Returns depth of this node (the root node has a depth of 0)
23483      * @return {Number}
23484      */
23485     getDepth : function(){
23486         var depth = 0;
23487         var p = this;
23488         while(p.parentNode){
23489             ++depth;
23490             p = p.parentNode;
23491         }
23492         return depth;
23493     },
23494
23495     // private
23496     setOwnerTree : function(tree){
23497         // if it's move, we need to update everyone
23498         if(tree != this.ownerTree){
23499             if(this.ownerTree){
23500                 this.ownerTree.unregisterNode(this);
23501             }
23502             this.ownerTree = tree;
23503             var cs = this.childNodes;
23504             for(var i = 0, len = cs.length; i < len; i++) {
23505                 cs[i].setOwnerTree(tree);
23506             }
23507             if(tree){
23508                 tree.registerNode(this);
23509             }
23510         }
23511     },
23512
23513     /**
23514      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23515      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23516      * @return {String} The path
23517      */
23518     getPath : function(attr){
23519         attr = attr || "id";
23520         var p = this.parentNode;
23521         var b = [this.attributes[attr]];
23522         while(p){
23523             b.unshift(p.attributes[attr]);
23524             p = p.parentNode;
23525         }
23526         var sep = this.getOwnerTree().pathSeparator;
23527         return sep + b.join(sep);
23528     },
23529
23530     /**
23531      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23532      * function call will be the scope provided or the current node. The arguments to the function
23533      * will be the args provided or the current node. If the function returns false at any point,
23534      * the bubble is stopped.
23535      * @param {Function} fn The function to call
23536      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23537      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23538      */
23539     bubble : function(fn, scope, args){
23540         var p = this;
23541         while(p){
23542             if(fn.call(scope || p, args || p) === false){
23543                 break;
23544             }
23545             p = p.parentNode;
23546         }
23547     },
23548
23549     /**
23550      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23551      * function call will be the scope provided or the current node. The arguments to the function
23552      * will be the args provided or the current node. If the function returns false at any point,
23553      * the cascade is stopped on that branch.
23554      * @param {Function} fn The function to call
23555      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23556      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23557      */
23558     cascade : function(fn, scope, args){
23559         if(fn.call(scope || this, args || this) !== false){
23560             var cs = this.childNodes;
23561             for(var i = 0, len = cs.length; i < len; i++) {
23562                 cs[i].cascade(fn, scope, args);
23563             }
23564         }
23565     },
23566
23567     /**
23568      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23569      * function call will be the scope provided or the current node. The arguments to the function
23570      * will be the args provided or the current node. If the function returns false at any point,
23571      * the iteration stops.
23572      * @param {Function} fn The function to call
23573      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23574      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23575      */
23576     eachChild : function(fn, scope, args){
23577         var cs = this.childNodes;
23578         for(var i = 0, len = cs.length; i < len; i++) {
23579                 if(fn.call(scope || this, args || cs[i]) === false){
23580                     break;
23581                 }
23582         }
23583     },
23584
23585     /**
23586      * Finds the first child that has the attribute with the specified value.
23587      * @param {String} attribute The attribute name
23588      * @param {Mixed} value The value to search for
23589      * @return {Node} The found child or null if none was found
23590      */
23591     findChild : function(attribute, value){
23592         var cs = this.childNodes;
23593         for(var i = 0, len = cs.length; i < len; i++) {
23594                 if(cs[i].attributes[attribute] == value){
23595                     return cs[i];
23596                 }
23597         }
23598         return null;
23599     },
23600
23601     /**
23602      * Finds the first child by a custom function. The child matches if the function passed
23603      * returns true.
23604      * @param {Function} fn
23605      * @param {Object} scope (optional)
23606      * @return {Node} The found child or null if none was found
23607      */
23608     findChildBy : function(fn, scope){
23609         var cs = this.childNodes;
23610         for(var i = 0, len = cs.length; i < len; i++) {
23611                 if(fn.call(scope||cs[i], cs[i]) === true){
23612                     return cs[i];
23613                 }
23614         }
23615         return null;
23616     },
23617
23618     /**
23619      * Sorts this nodes children using the supplied sort function
23620      * @param {Function} fn
23621      * @param {Object} scope (optional)
23622      */
23623     sort : function(fn, scope){
23624         var cs = this.childNodes;
23625         var len = cs.length;
23626         if(len > 0){
23627             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23628             cs.sort(sortFn);
23629             for(var i = 0; i < len; i++){
23630                 var n = cs[i];
23631                 n.previousSibling = cs[i-1];
23632                 n.nextSibling = cs[i+1];
23633                 if(i == 0){
23634                     this.setFirstChild(n);
23635                 }
23636                 if(i == len-1){
23637                     this.setLastChild(n);
23638                 }
23639             }
23640         }
23641     },
23642
23643     /**
23644      * Returns true if this node is an ancestor (at any point) of the passed node.
23645      * @param {Node} node
23646      * @return {Boolean}
23647      */
23648     contains : function(node){
23649         return node.isAncestor(this);
23650     },
23651
23652     /**
23653      * Returns true if the passed node is an ancestor (at any point) of this node.
23654      * @param {Node} node
23655      * @return {Boolean}
23656      */
23657     isAncestor : function(node){
23658         var p = this.parentNode;
23659         while(p){
23660             if(p == node){
23661                 return true;
23662             }
23663             p = p.parentNode;
23664         }
23665         return false;
23666     },
23667
23668     toString : function(){
23669         return "[Node"+(this.id?" "+this.id:"")+"]";
23670     }
23671 });/*
23672  * Based on:
23673  * Ext JS Library 1.1.1
23674  * Copyright(c) 2006-2007, Ext JS, LLC.
23675  *
23676  * Originally Released Under LGPL - original licence link has changed is not relivant.
23677  *
23678  * Fork - LGPL
23679  * <script type="text/javascript">
23680  */
23681  (function(){ 
23682 /**
23683  * @class Roo.Layer
23684  * @extends Roo.Element
23685  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23686  * automatic maintaining of shadow/shim positions.
23687  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23688  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23689  * you can pass a string with a CSS class name. False turns off the shadow.
23690  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23691  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23692  * @cfg {String} cls CSS class to add to the element
23693  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23694  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23695  * @constructor
23696  * @param {Object} config An object with config options.
23697  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23698  */
23699
23700 Roo.Layer = function(config, existingEl){
23701     config = config || {};
23702     var dh = Roo.DomHelper;
23703     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23704     if(existingEl){
23705         this.dom = Roo.getDom(existingEl);
23706     }
23707     if(!this.dom){
23708         var o = config.dh || {tag: "div", cls: "x-layer"};
23709         this.dom = dh.append(pel, o);
23710     }
23711     if(config.cls){
23712         this.addClass(config.cls);
23713     }
23714     this.constrain = config.constrain !== false;
23715     this.visibilityMode = Roo.Element.VISIBILITY;
23716     if(config.id){
23717         this.id = this.dom.id = config.id;
23718     }else{
23719         this.id = Roo.id(this.dom);
23720     }
23721     this.zindex = config.zindex || this.getZIndex();
23722     this.position("absolute", this.zindex);
23723     if(config.shadow){
23724         this.shadowOffset = config.shadowOffset || 4;
23725         this.shadow = new Roo.Shadow({
23726             offset : this.shadowOffset,
23727             mode : config.shadow
23728         });
23729     }else{
23730         this.shadowOffset = 0;
23731     }
23732     this.useShim = config.shim !== false && Roo.useShims;
23733     this.useDisplay = config.useDisplay;
23734     this.hide();
23735 };
23736
23737 var supr = Roo.Element.prototype;
23738
23739 // shims are shared among layer to keep from having 100 iframes
23740 var shims = [];
23741
23742 Roo.extend(Roo.Layer, Roo.Element, {
23743
23744     getZIndex : function(){
23745         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23746     },
23747
23748     getShim : function(){
23749         if(!this.useShim){
23750             return null;
23751         }
23752         if(this.shim){
23753             return this.shim;
23754         }
23755         var shim = shims.shift();
23756         if(!shim){
23757             shim = this.createShim();
23758             shim.enableDisplayMode('block');
23759             shim.dom.style.display = 'none';
23760             shim.dom.style.visibility = 'visible';
23761         }
23762         var pn = this.dom.parentNode;
23763         if(shim.dom.parentNode != pn){
23764             pn.insertBefore(shim.dom, this.dom);
23765         }
23766         shim.setStyle('z-index', this.getZIndex()-2);
23767         this.shim = shim;
23768         return shim;
23769     },
23770
23771     hideShim : function(){
23772         if(this.shim){
23773             this.shim.setDisplayed(false);
23774             shims.push(this.shim);
23775             delete this.shim;
23776         }
23777     },
23778
23779     disableShadow : function(){
23780         if(this.shadow){
23781             this.shadowDisabled = true;
23782             this.shadow.hide();
23783             this.lastShadowOffset = this.shadowOffset;
23784             this.shadowOffset = 0;
23785         }
23786     },
23787
23788     enableShadow : function(show){
23789         if(this.shadow){
23790             this.shadowDisabled = false;
23791             this.shadowOffset = this.lastShadowOffset;
23792             delete this.lastShadowOffset;
23793             if(show){
23794                 this.sync(true);
23795             }
23796         }
23797     },
23798
23799     // private
23800     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23801     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23802     sync : function(doShow){
23803         var sw = this.shadow;
23804         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23805             var sh = this.getShim();
23806
23807             var w = this.getWidth(),
23808                 h = this.getHeight();
23809
23810             var l = this.getLeft(true),
23811                 t = this.getTop(true);
23812
23813             if(sw && !this.shadowDisabled){
23814                 if(doShow && !sw.isVisible()){
23815                     sw.show(this);
23816                 }else{
23817                     sw.realign(l, t, w, h);
23818                 }
23819                 if(sh){
23820                     if(doShow){
23821                        sh.show();
23822                     }
23823                     // fit the shim behind the shadow, so it is shimmed too
23824                     var a = sw.adjusts, s = sh.dom.style;
23825                     s.left = (Math.min(l, l+a.l))+"px";
23826                     s.top = (Math.min(t, t+a.t))+"px";
23827                     s.width = (w+a.w)+"px";
23828                     s.height = (h+a.h)+"px";
23829                 }
23830             }else if(sh){
23831                 if(doShow){
23832                    sh.show();
23833                 }
23834                 sh.setSize(w, h);
23835                 sh.setLeftTop(l, t);
23836             }
23837             
23838         }
23839     },
23840
23841     // private
23842     destroy : function(){
23843         this.hideShim();
23844         if(this.shadow){
23845             this.shadow.hide();
23846         }
23847         this.removeAllListeners();
23848         var pn = this.dom.parentNode;
23849         if(pn){
23850             pn.removeChild(this.dom);
23851         }
23852         Roo.Element.uncache(this.id);
23853     },
23854
23855     remove : function(){
23856         this.destroy();
23857     },
23858
23859     // private
23860     beginUpdate : function(){
23861         this.updating = true;
23862     },
23863
23864     // private
23865     endUpdate : function(){
23866         this.updating = false;
23867         this.sync(true);
23868     },
23869
23870     // private
23871     hideUnders : function(negOffset){
23872         if(this.shadow){
23873             this.shadow.hide();
23874         }
23875         this.hideShim();
23876     },
23877
23878     // private
23879     constrainXY : function(){
23880         if(this.constrain){
23881             var vw = Roo.lib.Dom.getViewWidth(),
23882                 vh = Roo.lib.Dom.getViewHeight();
23883             var s = Roo.get(document).getScroll();
23884
23885             var xy = this.getXY();
23886             var x = xy[0], y = xy[1];   
23887             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23888             // only move it if it needs it
23889             var moved = false;
23890             // first validate right/bottom
23891             if((x + w) > vw+s.left){
23892                 x = vw - w - this.shadowOffset;
23893                 moved = true;
23894             }
23895             if((y + h) > vh+s.top){
23896                 y = vh - h - this.shadowOffset;
23897                 moved = true;
23898             }
23899             // then make sure top/left isn't negative
23900             if(x < s.left){
23901                 x = s.left;
23902                 moved = true;
23903             }
23904             if(y < s.top){
23905                 y = s.top;
23906                 moved = true;
23907             }
23908             if(moved){
23909                 if(this.avoidY){
23910                     var ay = this.avoidY;
23911                     if(y <= ay && (y+h) >= ay){
23912                         y = ay-h-5;   
23913                     }
23914                 }
23915                 xy = [x, y];
23916                 this.storeXY(xy);
23917                 supr.setXY.call(this, xy);
23918                 this.sync();
23919             }
23920         }
23921     },
23922
23923     isVisible : function(){
23924         return this.visible;    
23925     },
23926
23927     // private
23928     showAction : function(){
23929         this.visible = true; // track visibility to prevent getStyle calls
23930         if(this.useDisplay === true){
23931             this.setDisplayed("");
23932         }else if(this.lastXY){
23933             supr.setXY.call(this, this.lastXY);
23934         }else if(this.lastLT){
23935             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23936         }
23937     },
23938
23939     // private
23940     hideAction : function(){
23941         this.visible = false;
23942         if(this.useDisplay === true){
23943             this.setDisplayed(false);
23944         }else{
23945             this.setLeftTop(-10000,-10000);
23946         }
23947     },
23948
23949     // overridden Element method
23950     setVisible : function(v, a, d, c, e){
23951         if(v){
23952             this.showAction();
23953         }
23954         if(a && v){
23955             var cb = function(){
23956                 this.sync(true);
23957                 if(c){
23958                     c();
23959                 }
23960             }.createDelegate(this);
23961             supr.setVisible.call(this, true, true, d, cb, e);
23962         }else{
23963             if(!v){
23964                 this.hideUnders(true);
23965             }
23966             var cb = c;
23967             if(a){
23968                 cb = function(){
23969                     this.hideAction();
23970                     if(c){
23971                         c();
23972                     }
23973                 }.createDelegate(this);
23974             }
23975             supr.setVisible.call(this, v, a, d, cb, e);
23976             if(v){
23977                 this.sync(true);
23978             }else if(!a){
23979                 this.hideAction();
23980             }
23981         }
23982     },
23983
23984     storeXY : function(xy){
23985         delete this.lastLT;
23986         this.lastXY = xy;
23987     },
23988
23989     storeLeftTop : function(left, top){
23990         delete this.lastXY;
23991         this.lastLT = [left, top];
23992     },
23993
23994     // private
23995     beforeFx : function(){
23996         this.beforeAction();
23997         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23998     },
23999
24000     // private
24001     afterFx : function(){
24002         Roo.Layer.superclass.afterFx.apply(this, arguments);
24003         this.sync(this.isVisible());
24004     },
24005
24006     // private
24007     beforeAction : function(){
24008         if(!this.updating && this.shadow){
24009             this.shadow.hide();
24010         }
24011     },
24012
24013     // overridden Element method
24014     setLeft : function(left){
24015         this.storeLeftTop(left, this.getTop(true));
24016         supr.setLeft.apply(this, arguments);
24017         this.sync();
24018     },
24019
24020     setTop : function(top){
24021         this.storeLeftTop(this.getLeft(true), top);
24022         supr.setTop.apply(this, arguments);
24023         this.sync();
24024     },
24025
24026     setLeftTop : function(left, top){
24027         this.storeLeftTop(left, top);
24028         supr.setLeftTop.apply(this, arguments);
24029         this.sync();
24030     },
24031
24032     setXY : function(xy, a, d, c, e){
24033         this.fixDisplay();
24034         this.beforeAction();
24035         this.storeXY(xy);
24036         var cb = this.createCB(c);
24037         supr.setXY.call(this, xy, a, d, cb, e);
24038         if(!a){
24039             cb();
24040         }
24041     },
24042
24043     // private
24044     createCB : function(c){
24045         var el = this;
24046         return function(){
24047             el.constrainXY();
24048             el.sync(true);
24049             if(c){
24050                 c();
24051             }
24052         };
24053     },
24054
24055     // overridden Element method
24056     setX : function(x, a, d, c, e){
24057         this.setXY([x, this.getY()], a, d, c, e);
24058     },
24059
24060     // overridden Element method
24061     setY : function(y, a, d, c, e){
24062         this.setXY([this.getX(), y], a, d, c, e);
24063     },
24064
24065     // overridden Element method
24066     setSize : function(w, h, a, d, c, e){
24067         this.beforeAction();
24068         var cb = this.createCB(c);
24069         supr.setSize.call(this, w, h, a, d, cb, e);
24070         if(!a){
24071             cb();
24072         }
24073     },
24074
24075     // overridden Element method
24076     setWidth : function(w, a, d, c, e){
24077         this.beforeAction();
24078         var cb = this.createCB(c);
24079         supr.setWidth.call(this, w, a, d, cb, e);
24080         if(!a){
24081             cb();
24082         }
24083     },
24084
24085     // overridden Element method
24086     setHeight : function(h, a, d, c, e){
24087         this.beforeAction();
24088         var cb = this.createCB(c);
24089         supr.setHeight.call(this, h, a, d, cb, e);
24090         if(!a){
24091             cb();
24092         }
24093     },
24094
24095     // overridden Element method
24096     setBounds : function(x, y, w, h, a, d, c, e){
24097         this.beforeAction();
24098         var cb = this.createCB(c);
24099         if(!a){
24100             this.storeXY([x, y]);
24101             supr.setXY.call(this, [x, y]);
24102             supr.setSize.call(this, w, h, a, d, cb, e);
24103             cb();
24104         }else{
24105             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24106         }
24107         return this;
24108     },
24109     
24110     /**
24111      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24112      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24113      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24114      * @param {Number} zindex The new z-index to set
24115      * @return {this} The Layer
24116      */
24117     setZIndex : function(zindex){
24118         this.zindex = zindex;
24119         this.setStyle("z-index", zindex + 2);
24120         if(this.shadow){
24121             this.shadow.setZIndex(zindex + 1);
24122         }
24123         if(this.shim){
24124             this.shim.setStyle("z-index", zindex);
24125         }
24126     }
24127 });
24128 })();/*
24129  * Based on:
24130  * Ext JS Library 1.1.1
24131  * Copyright(c) 2006-2007, Ext JS, LLC.
24132  *
24133  * Originally Released Under LGPL - original licence link has changed is not relivant.
24134  *
24135  * Fork - LGPL
24136  * <script type="text/javascript">
24137  */
24138
24139
24140 /**
24141  * @class Roo.Shadow
24142  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24143  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24144  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24145  * @constructor
24146  * Create a new Shadow
24147  * @param {Object} config The config object
24148  */
24149 Roo.Shadow = function(config){
24150     Roo.apply(this, config);
24151     if(typeof this.mode != "string"){
24152         this.mode = this.defaultMode;
24153     }
24154     var o = this.offset, a = {h: 0};
24155     var rad = Math.floor(this.offset/2);
24156     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24157         case "drop":
24158             a.w = 0;
24159             a.l = a.t = o;
24160             a.t -= 1;
24161             if(Roo.isIE){
24162                 a.l -= this.offset + rad;
24163                 a.t -= this.offset + rad;
24164                 a.w -= rad;
24165                 a.h -= rad;
24166                 a.t += 1;
24167             }
24168         break;
24169         case "sides":
24170             a.w = (o*2);
24171             a.l = -o;
24172             a.t = o-1;
24173             if(Roo.isIE){
24174                 a.l -= (this.offset - rad);
24175                 a.t -= this.offset + rad;
24176                 a.l += 1;
24177                 a.w -= (this.offset - rad)*2;
24178                 a.w -= rad + 1;
24179                 a.h -= 1;
24180             }
24181         break;
24182         case "frame":
24183             a.w = a.h = (o*2);
24184             a.l = a.t = -o;
24185             a.t += 1;
24186             a.h -= 2;
24187             if(Roo.isIE){
24188                 a.l -= (this.offset - rad);
24189                 a.t -= (this.offset - rad);
24190                 a.l += 1;
24191                 a.w -= (this.offset + rad + 1);
24192                 a.h -= (this.offset + rad);
24193                 a.h += 1;
24194             }
24195         break;
24196     };
24197
24198     this.adjusts = a;
24199 };
24200
24201 Roo.Shadow.prototype = {
24202     /**
24203      * @cfg {String} mode
24204      * The shadow display mode.  Supports the following options:<br />
24205      * sides: Shadow displays on both sides and bottom only<br />
24206      * frame: Shadow displays equally on all four sides<br />
24207      * drop: Traditional bottom-right drop shadow (default)
24208      */
24209     /**
24210      * @cfg {String} offset
24211      * The number of pixels to offset the shadow from the element (defaults to 4)
24212      */
24213     offset: 4,
24214
24215     // private
24216     defaultMode: "drop",
24217
24218     /**
24219      * Displays the shadow under the target element
24220      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24221      */
24222     show : function(target){
24223         target = Roo.get(target);
24224         if(!this.el){
24225             this.el = Roo.Shadow.Pool.pull();
24226             if(this.el.dom.nextSibling != target.dom){
24227                 this.el.insertBefore(target);
24228             }
24229         }
24230         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24231         if(Roo.isIE){
24232             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24233         }
24234         this.realign(
24235             target.getLeft(true),
24236             target.getTop(true),
24237             target.getWidth(),
24238             target.getHeight()
24239         );
24240         this.el.dom.style.display = "block";
24241     },
24242
24243     /**
24244      * Returns true if the shadow is visible, else false
24245      */
24246     isVisible : function(){
24247         return this.el ? true : false;  
24248     },
24249
24250     /**
24251      * Direct alignment when values are already available. Show must be called at least once before
24252      * calling this method to ensure it is initialized.
24253      * @param {Number} left The target element left position
24254      * @param {Number} top The target element top position
24255      * @param {Number} width The target element width
24256      * @param {Number} height The target element height
24257      */
24258     realign : function(l, t, w, h){
24259         if(!this.el){
24260             return;
24261         }
24262         var a = this.adjusts, d = this.el.dom, s = d.style;
24263         var iea = 0;
24264         s.left = (l+a.l)+"px";
24265         s.top = (t+a.t)+"px";
24266         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24267  
24268         if(s.width != sws || s.height != shs){
24269             s.width = sws;
24270             s.height = shs;
24271             if(!Roo.isIE){
24272                 var cn = d.childNodes;
24273                 var sww = Math.max(0, (sw-12))+"px";
24274                 cn[0].childNodes[1].style.width = sww;
24275                 cn[1].childNodes[1].style.width = sww;
24276                 cn[2].childNodes[1].style.width = sww;
24277                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24278             }
24279         }
24280     },
24281
24282     /**
24283      * Hides this shadow
24284      */
24285     hide : function(){
24286         if(this.el){
24287             this.el.dom.style.display = "none";
24288             Roo.Shadow.Pool.push(this.el);
24289             delete this.el;
24290         }
24291     },
24292
24293     /**
24294      * Adjust the z-index of this shadow
24295      * @param {Number} zindex The new z-index
24296      */
24297     setZIndex : function(z){
24298         this.zIndex = z;
24299         if(this.el){
24300             this.el.setStyle("z-index", z);
24301         }
24302     }
24303 };
24304
24305 // Private utility class that manages the internal Shadow cache
24306 Roo.Shadow.Pool = function(){
24307     var p = [];
24308     var markup = Roo.isIE ?
24309                  '<div class="x-ie-shadow"></div>' :
24310                  '<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>';
24311     return {
24312         pull : function(){
24313             var sh = p.shift();
24314             if(!sh){
24315                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24316                 sh.autoBoxAdjust = false;
24317             }
24318             return sh;
24319         },
24320
24321         push : function(sh){
24322             p.push(sh);
24323         }
24324     };
24325 }();/*
24326  * Based on:
24327  * Ext JS Library 1.1.1
24328  * Copyright(c) 2006-2007, Ext JS, LLC.
24329  *
24330  * Originally Released Under LGPL - original licence link has changed is not relivant.
24331  *
24332  * Fork - LGPL
24333  * <script type="text/javascript">
24334  */
24335
24336
24337 /**
24338  * @class Roo.SplitBar
24339  * @extends Roo.util.Observable
24340  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24341  * <br><br>
24342  * Usage:
24343  * <pre><code>
24344 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24345                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24346 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24347 split.minSize = 100;
24348 split.maxSize = 600;
24349 split.animate = true;
24350 split.on('moved', splitterMoved);
24351 </code></pre>
24352  * @constructor
24353  * Create a new SplitBar
24354  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24355  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24356  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24357  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24358                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24359                         position of the SplitBar).
24360  */
24361 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24362     
24363     /** @private */
24364     this.el = Roo.get(dragElement, true);
24365     this.el.dom.unselectable = "on";
24366     /** @private */
24367     this.resizingEl = Roo.get(resizingElement, true);
24368
24369     /**
24370      * @private
24371      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24372      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24373      * @type Number
24374      */
24375     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24376     
24377     /**
24378      * The minimum size of the resizing element. (Defaults to 0)
24379      * @type Number
24380      */
24381     this.minSize = 0;
24382     
24383     /**
24384      * The maximum size of the resizing element. (Defaults to 2000)
24385      * @type Number
24386      */
24387     this.maxSize = 2000;
24388     
24389     /**
24390      * Whether to animate the transition to the new size
24391      * @type Boolean
24392      */
24393     this.animate = false;
24394     
24395     /**
24396      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24397      * @type Boolean
24398      */
24399     this.useShim = false;
24400     
24401     /** @private */
24402     this.shim = null;
24403     
24404     if(!existingProxy){
24405         /** @private */
24406         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24407     }else{
24408         this.proxy = Roo.get(existingProxy).dom;
24409     }
24410     /** @private */
24411     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24412     
24413     /** @private */
24414     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24415     
24416     /** @private */
24417     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24418     
24419     /** @private */
24420     this.dragSpecs = {};
24421     
24422     /**
24423      * @private The adapter to use to positon and resize elements
24424      */
24425     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24426     this.adapter.init(this);
24427     
24428     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24429         /** @private */
24430         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24431         this.el.addClass("x-splitbar-h");
24432     }else{
24433         /** @private */
24434         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24435         this.el.addClass("x-splitbar-v");
24436     }
24437     
24438     this.addEvents({
24439         /**
24440          * @event resize
24441          * Fires when the splitter is moved (alias for {@link #event-moved})
24442          * @param {Roo.SplitBar} this
24443          * @param {Number} newSize the new width or height
24444          */
24445         "resize" : true,
24446         /**
24447          * @event moved
24448          * Fires when the splitter is moved
24449          * @param {Roo.SplitBar} this
24450          * @param {Number} newSize the new width or height
24451          */
24452         "moved" : true,
24453         /**
24454          * @event beforeresize
24455          * Fires before the splitter is dragged
24456          * @param {Roo.SplitBar} this
24457          */
24458         "beforeresize" : true,
24459
24460         "beforeapply" : true
24461     });
24462
24463     Roo.util.Observable.call(this);
24464 };
24465
24466 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24467     onStartProxyDrag : function(x, y){
24468         this.fireEvent("beforeresize", this);
24469         if(!this.overlay){
24470             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24471             o.unselectable();
24472             o.enableDisplayMode("block");
24473             // all splitbars share the same overlay
24474             Roo.SplitBar.prototype.overlay = o;
24475         }
24476         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24477         this.overlay.show();
24478         Roo.get(this.proxy).setDisplayed("block");
24479         var size = this.adapter.getElementSize(this);
24480         this.activeMinSize = this.getMinimumSize();;
24481         this.activeMaxSize = this.getMaximumSize();;
24482         var c1 = size - this.activeMinSize;
24483         var c2 = Math.max(this.activeMaxSize - size, 0);
24484         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24485             this.dd.resetConstraints();
24486             this.dd.setXConstraint(
24487                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24488                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24489             );
24490             this.dd.setYConstraint(0, 0);
24491         }else{
24492             this.dd.resetConstraints();
24493             this.dd.setXConstraint(0, 0);
24494             this.dd.setYConstraint(
24495                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24496                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24497             );
24498          }
24499         this.dragSpecs.startSize = size;
24500         this.dragSpecs.startPoint = [x, y];
24501         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24502     },
24503     
24504     /** 
24505      * @private Called after the drag operation by the DDProxy
24506      */
24507     onEndProxyDrag : function(e){
24508         Roo.get(this.proxy).setDisplayed(false);
24509         var endPoint = Roo.lib.Event.getXY(e);
24510         if(this.overlay){
24511             this.overlay.hide();
24512         }
24513         var newSize;
24514         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24515             newSize = this.dragSpecs.startSize + 
24516                 (this.placement == Roo.SplitBar.LEFT ?
24517                     endPoint[0] - this.dragSpecs.startPoint[0] :
24518                     this.dragSpecs.startPoint[0] - endPoint[0]
24519                 );
24520         }else{
24521             newSize = this.dragSpecs.startSize + 
24522                 (this.placement == Roo.SplitBar.TOP ?
24523                     endPoint[1] - this.dragSpecs.startPoint[1] :
24524                     this.dragSpecs.startPoint[1] - endPoint[1]
24525                 );
24526         }
24527         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24528         if(newSize != this.dragSpecs.startSize){
24529             if(this.fireEvent('beforeapply', this, newSize) !== false){
24530                 this.adapter.setElementSize(this, newSize);
24531                 this.fireEvent("moved", this, newSize);
24532                 this.fireEvent("resize", this, newSize);
24533             }
24534         }
24535     },
24536     
24537     /**
24538      * Get the adapter this SplitBar uses
24539      * @return The adapter object
24540      */
24541     getAdapter : function(){
24542         return this.adapter;
24543     },
24544     
24545     /**
24546      * Set the adapter this SplitBar uses
24547      * @param {Object} adapter A SplitBar adapter object
24548      */
24549     setAdapter : function(adapter){
24550         this.adapter = adapter;
24551         this.adapter.init(this);
24552     },
24553     
24554     /**
24555      * Gets the minimum size for the resizing element
24556      * @return {Number} The minimum size
24557      */
24558     getMinimumSize : function(){
24559         return this.minSize;
24560     },
24561     
24562     /**
24563      * Sets the minimum size for the resizing element
24564      * @param {Number} minSize The minimum size
24565      */
24566     setMinimumSize : function(minSize){
24567         this.minSize = minSize;
24568     },
24569     
24570     /**
24571      * Gets the maximum size for the resizing element
24572      * @return {Number} The maximum size
24573      */
24574     getMaximumSize : function(){
24575         return this.maxSize;
24576     },
24577     
24578     /**
24579      * Sets the maximum size for the resizing element
24580      * @param {Number} maxSize The maximum size
24581      */
24582     setMaximumSize : function(maxSize){
24583         this.maxSize = maxSize;
24584     },
24585     
24586     /**
24587      * Sets the initialize size for the resizing element
24588      * @param {Number} size The initial size
24589      */
24590     setCurrentSize : function(size){
24591         var oldAnimate = this.animate;
24592         this.animate = false;
24593         this.adapter.setElementSize(this, size);
24594         this.animate = oldAnimate;
24595     },
24596     
24597     /**
24598      * Destroy this splitbar. 
24599      * @param {Boolean} removeEl True to remove the element
24600      */
24601     destroy : function(removeEl){
24602         if(this.shim){
24603             this.shim.remove();
24604         }
24605         this.dd.unreg();
24606         this.proxy.parentNode.removeChild(this.proxy);
24607         if(removeEl){
24608             this.el.remove();
24609         }
24610     }
24611 });
24612
24613 /**
24614  * @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.
24615  */
24616 Roo.SplitBar.createProxy = function(dir){
24617     var proxy = new Roo.Element(document.createElement("div"));
24618     proxy.unselectable();
24619     var cls = 'x-splitbar-proxy';
24620     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24621     document.body.appendChild(proxy.dom);
24622     return proxy.dom;
24623 };
24624
24625 /** 
24626  * @class Roo.SplitBar.BasicLayoutAdapter
24627  * Default Adapter. It assumes the splitter and resizing element are not positioned
24628  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24629  */
24630 Roo.SplitBar.BasicLayoutAdapter = function(){
24631 };
24632
24633 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24634     // do nothing for now
24635     init : function(s){
24636     
24637     },
24638     /**
24639      * Called before drag operations to get the current size of the resizing element. 
24640      * @param {Roo.SplitBar} s The SplitBar using this adapter
24641      */
24642      getElementSize : function(s){
24643         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24644             return s.resizingEl.getWidth();
24645         }else{
24646             return s.resizingEl.getHeight();
24647         }
24648     },
24649     
24650     /**
24651      * Called after drag operations to set the size of the resizing element.
24652      * @param {Roo.SplitBar} s The SplitBar using this adapter
24653      * @param {Number} newSize The new size to set
24654      * @param {Function} onComplete A function to be invoked when resizing is complete
24655      */
24656     setElementSize : function(s, newSize, onComplete){
24657         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24658             if(!s.animate){
24659                 s.resizingEl.setWidth(newSize);
24660                 if(onComplete){
24661                     onComplete(s, newSize);
24662                 }
24663             }else{
24664                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24665             }
24666         }else{
24667             
24668             if(!s.animate){
24669                 s.resizingEl.setHeight(newSize);
24670                 if(onComplete){
24671                     onComplete(s, newSize);
24672                 }
24673             }else{
24674                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24675             }
24676         }
24677     }
24678 };
24679
24680 /** 
24681  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24682  * @extends Roo.SplitBar.BasicLayoutAdapter
24683  * Adapter that  moves the splitter element to align with the resized sizing element. 
24684  * Used with an absolute positioned SplitBar.
24685  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24686  * document.body, make sure you assign an id to the body element.
24687  */
24688 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24689     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24690     this.container = Roo.get(container);
24691 };
24692
24693 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24694     init : function(s){
24695         this.basic.init(s);
24696     },
24697     
24698     getElementSize : function(s){
24699         return this.basic.getElementSize(s);
24700     },
24701     
24702     setElementSize : function(s, newSize, onComplete){
24703         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24704     },
24705     
24706     moveSplitter : function(s){
24707         var yes = Roo.SplitBar;
24708         switch(s.placement){
24709             case yes.LEFT:
24710                 s.el.setX(s.resizingEl.getRight());
24711                 break;
24712             case yes.RIGHT:
24713                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24714                 break;
24715             case yes.TOP:
24716                 s.el.setY(s.resizingEl.getBottom());
24717                 break;
24718             case yes.BOTTOM:
24719                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24720                 break;
24721         }
24722     }
24723 };
24724
24725 /**
24726  * Orientation constant - Create a vertical SplitBar
24727  * @static
24728  * @type Number
24729  */
24730 Roo.SplitBar.VERTICAL = 1;
24731
24732 /**
24733  * Orientation constant - Create a horizontal SplitBar
24734  * @static
24735  * @type Number
24736  */
24737 Roo.SplitBar.HORIZONTAL = 2;
24738
24739 /**
24740  * Placement constant - The resizing element is to the left of the splitter element
24741  * @static
24742  * @type Number
24743  */
24744 Roo.SplitBar.LEFT = 1;
24745
24746 /**
24747  * Placement constant - The resizing element is to the right of the splitter element
24748  * @static
24749  * @type Number
24750  */
24751 Roo.SplitBar.RIGHT = 2;
24752
24753 /**
24754  * Placement constant - The resizing element is positioned above the splitter element
24755  * @static
24756  * @type Number
24757  */
24758 Roo.SplitBar.TOP = 3;
24759
24760 /**
24761  * Placement constant - The resizing element is positioned under splitter element
24762  * @static
24763  * @type Number
24764  */
24765 Roo.SplitBar.BOTTOM = 4;
24766 /*
24767  * Based on:
24768  * Ext JS Library 1.1.1
24769  * Copyright(c) 2006-2007, Ext JS, LLC.
24770  *
24771  * Originally Released Under LGPL - original licence link has changed is not relivant.
24772  *
24773  * Fork - LGPL
24774  * <script type="text/javascript">
24775  */
24776
24777 /**
24778  * @class Roo.View
24779  * @extends Roo.util.Observable
24780  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24781  * This class also supports single and multi selection modes. <br>
24782  * Create a data model bound view:
24783  <pre><code>
24784  var store = new Roo.data.Store(...);
24785
24786  var view = new Roo.View({
24787     el : "my-element",
24788     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24789  
24790     singleSelect: true,
24791     selectedClass: "ydataview-selected",
24792     store: store
24793  });
24794
24795  // listen for node click?
24796  view.on("click", function(vw, index, node, e){
24797  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24798  });
24799
24800  // load XML data
24801  dataModel.load("foobar.xml");
24802  </code></pre>
24803  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24804  * <br><br>
24805  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24806  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24807  * 
24808  * Note: old style constructor is still suported (container, template, config)
24809  * 
24810  * @constructor
24811  * Create a new View
24812  * @param {Object} config The config object
24813  * 
24814  */
24815 Roo.View = function(config, depreciated_tpl, depreciated_config){
24816     
24817     if (typeof(depreciated_tpl) == 'undefined') {
24818         // new way.. - universal constructor.
24819         Roo.apply(this, config);
24820         this.el  = Roo.get(this.el);
24821     } else {
24822         // old format..
24823         this.el  = Roo.get(config);
24824         this.tpl = depreciated_tpl;
24825         Roo.apply(this, depreciated_config);
24826     }
24827     this.wrapEl  = this.el.wrap().wrap();
24828     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24829     
24830     
24831     if(typeof(this.tpl) == "string"){
24832         this.tpl = new Roo.Template(this.tpl);
24833     } else {
24834         // support xtype ctors..
24835         this.tpl = new Roo.factory(this.tpl, Roo);
24836     }
24837     
24838     
24839     this.tpl.compile();
24840    
24841   
24842     
24843      
24844     /** @private */
24845     this.addEvents({
24846         /**
24847          * @event beforeclick
24848          * Fires before a click is processed. Returns false to cancel the default action.
24849          * @param {Roo.View} this
24850          * @param {Number} index The index of the target node
24851          * @param {HTMLElement} node The target node
24852          * @param {Roo.EventObject} e The raw event object
24853          */
24854             "beforeclick" : true,
24855         /**
24856          * @event click
24857          * Fires when a template node is clicked.
24858          * @param {Roo.View} this
24859          * @param {Number} index The index of the target node
24860          * @param {HTMLElement} node The target node
24861          * @param {Roo.EventObject} e The raw event object
24862          */
24863             "click" : true,
24864         /**
24865          * @event dblclick
24866          * Fires when a template node is double clicked.
24867          * @param {Roo.View} this
24868          * @param {Number} index The index of the target node
24869          * @param {HTMLElement} node The target node
24870          * @param {Roo.EventObject} e The raw event object
24871          */
24872             "dblclick" : true,
24873         /**
24874          * @event contextmenu
24875          * Fires when a template node is right clicked.
24876          * @param {Roo.View} this
24877          * @param {Number} index The index of the target node
24878          * @param {HTMLElement} node The target node
24879          * @param {Roo.EventObject} e The raw event object
24880          */
24881             "contextmenu" : true,
24882         /**
24883          * @event selectionchange
24884          * Fires when the selected nodes change.
24885          * @param {Roo.View} this
24886          * @param {Array} selections Array of the selected nodes
24887          */
24888             "selectionchange" : true,
24889     
24890         /**
24891          * @event beforeselect
24892          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24893          * @param {Roo.View} this
24894          * @param {HTMLElement} node The node to be selected
24895          * @param {Array} selections Array of currently selected nodes
24896          */
24897             "beforeselect" : true,
24898         /**
24899          * @event preparedata
24900          * Fires on every row to render, to allow you to change the data.
24901          * @param {Roo.View} this
24902          * @param {Object} data to be rendered (change this)
24903          */
24904           "preparedata" : true
24905           
24906           
24907         });
24908
24909
24910
24911     this.el.on({
24912         "click": this.onClick,
24913         "dblclick": this.onDblClick,
24914         "contextmenu": this.onContextMenu,
24915         scope:this
24916     });
24917
24918     this.selections = [];
24919     this.nodes = [];
24920     this.cmp = new Roo.CompositeElementLite([]);
24921     if(this.store){
24922         this.store = Roo.factory(this.store, Roo.data);
24923         this.setStore(this.store, true);
24924     }
24925     
24926     if ( this.footer && this.footer.xtype) {
24927            
24928          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24929         
24930         this.footer.dataSource = this.store
24931         this.footer.container = fctr;
24932         this.footer = Roo.factory(this.footer, Roo);
24933         fctr.insertFirst(this.el);
24934         
24935         // this is a bit insane - as the paging toolbar seems to detach the el..
24936 //        dom.parentNode.parentNode.parentNode
24937          // they get detached?
24938     }
24939     
24940     
24941     Roo.View.superclass.constructor.call(this);
24942     
24943     
24944 };
24945
24946 Roo.extend(Roo.View, Roo.util.Observable, {
24947     
24948      /**
24949      * @cfg {Roo.data.Store} store Data store to load data from.
24950      */
24951     store : false,
24952     
24953     /**
24954      * @cfg {String|Roo.Element} el The container element.
24955      */
24956     el : '',
24957     
24958     /**
24959      * @cfg {String|Roo.Template} tpl The template used by this View 
24960      */
24961     tpl : false,
24962     /**
24963      * @cfg {String} dataName the named area of the template to use as the data area
24964      *                          Works with domtemplates roo-name="name"
24965      */
24966     dataName: false,
24967     /**
24968      * @cfg {String} selectedClass The css class to add to selected nodes
24969      */
24970     selectedClass : "x-view-selected",
24971      /**
24972      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24973      */
24974     emptyText : "",
24975     
24976     /**
24977      * @cfg {String} text to display on mask (default Loading)
24978      */
24979     mask : false,
24980     /**
24981      * @cfg {Boolean} multiSelect Allow multiple selection
24982      */
24983     multiSelect : false,
24984     /**
24985      * @cfg {Boolean} singleSelect Allow single selection
24986      */
24987     singleSelect:  false,
24988     
24989     /**
24990      * @cfg {Boolean} toggleSelect - selecting 
24991      */
24992     toggleSelect : false,
24993     
24994     /**
24995      * Returns the element this view is bound to.
24996      * @return {Roo.Element}
24997      */
24998     getEl : function(){
24999         return this.wrapEl;
25000     },
25001     
25002     
25003
25004     /**
25005      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25006      */
25007     refresh : function(){
25008         Roo.log('refresh');
25009         var t = this.tpl;
25010         
25011         // if we are using something like 'domtemplate', then
25012         // the what gets used is:
25013         // t.applySubtemplate(NAME, data, wrapping data..)
25014         // the outer template then get' applied with
25015         //     the store 'extra data'
25016         // and the body get's added to the
25017         //      roo-name="data" node?
25018         //      <span class='roo-tpl-{name}'></span> ?????
25019         
25020         
25021         
25022         this.clearSelections();
25023         this.el.update("");
25024         var html = [];
25025         var records = this.store.getRange();
25026         if(records.length < 1) {
25027             
25028             // is this valid??  = should it render a template??
25029             
25030             this.el.update(this.emptyText);
25031             return;
25032         }
25033         var el = this.el;
25034         if (this.dataName) {
25035             this.el.update(t.apply(this.store.meta)); //????
25036             el = this.el.child('.roo-tpl-' + this.dataName);
25037         }
25038         
25039         for(var i = 0, len = records.length; i < len; i++){
25040             var data = this.prepareData(records[i].data, i, records[i]);
25041             this.fireEvent("preparedata", this, data, i, records[i]);
25042             html[html.length] = Roo.util.Format.trim(
25043                 this.dataName ?
25044                     t.applySubtemplate(this.dataName, data, this.store.meta) :
25045                     t.apply(data)
25046             );
25047         }
25048         
25049         
25050         
25051         el.update(html.join(""));
25052         this.nodes = el.dom.childNodes;
25053         this.updateIndexes(0);
25054     },
25055     
25056
25057     /**
25058      * Function to override to reformat the data that is sent to
25059      * the template for each node.
25060      * DEPRICATED - use the preparedata event handler.
25061      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25062      * a JSON object for an UpdateManager bound view).
25063      */
25064     prepareData : function(data, index, record)
25065     {
25066         this.fireEvent("preparedata", this, data, index, record);
25067         return data;
25068     },
25069
25070     onUpdate : function(ds, record){
25071          Roo.log('on update');   
25072         this.clearSelections();
25073         var index = this.store.indexOf(record);
25074         var n = this.nodes[index];
25075         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25076         n.parentNode.removeChild(n);
25077         this.updateIndexes(index, index);
25078     },
25079
25080     
25081     
25082 // --------- FIXME     
25083     onAdd : function(ds, records, index)
25084     {
25085         Roo.log(['on Add', ds, records, index] );        
25086         this.clearSelections();
25087         if(this.nodes.length == 0){
25088             this.refresh();
25089             return;
25090         }
25091         var n = this.nodes[index];
25092         for(var i = 0, len = records.length; i < len; i++){
25093             var d = this.prepareData(records[i].data, i, records[i]);
25094             if(n){
25095                 this.tpl.insertBefore(n, d);
25096             }else{
25097                 
25098                 this.tpl.append(this.el, d);
25099             }
25100         }
25101         this.updateIndexes(index);
25102     },
25103
25104     onRemove : function(ds, record, index){
25105         Roo.log('onRemove');
25106         this.clearSelections();
25107         var el = this.dataName  ?
25108             this.el.child('.roo-tpl-' + this.dataName) :
25109             this.el; 
25110         
25111         el.dom.removeChild(this.nodes[index]);
25112         this.updateIndexes(index);
25113     },
25114
25115     /**
25116      * Refresh an individual node.
25117      * @param {Number} index
25118      */
25119     refreshNode : function(index){
25120         this.onUpdate(this.store, this.store.getAt(index));
25121     },
25122
25123     updateIndexes : function(startIndex, endIndex){
25124         var ns = this.nodes;
25125         startIndex = startIndex || 0;
25126         endIndex = endIndex || ns.length - 1;
25127         for(var i = startIndex; i <= endIndex; i++){
25128             ns[i].nodeIndex = i;
25129         }
25130     },
25131
25132     /**
25133      * Changes the data store this view uses and refresh the view.
25134      * @param {Store} store
25135      */
25136     setStore : function(store, initial){
25137         if(!initial && this.store){
25138             this.store.un("datachanged", this.refresh);
25139             this.store.un("add", this.onAdd);
25140             this.store.un("remove", this.onRemove);
25141             this.store.un("update", this.onUpdate);
25142             this.store.un("clear", this.refresh);
25143             this.store.un("beforeload", this.onBeforeLoad);
25144             this.store.un("load", this.onLoad);
25145             this.store.un("loadexception", this.onLoad);
25146         }
25147         if(store){
25148           
25149             store.on("datachanged", this.refresh, this);
25150             store.on("add", this.onAdd, this);
25151             store.on("remove", this.onRemove, this);
25152             store.on("update", this.onUpdate, this);
25153             store.on("clear", this.refresh, this);
25154             store.on("beforeload", this.onBeforeLoad, this);
25155             store.on("load", this.onLoad, this);
25156             store.on("loadexception", this.onLoad, this);
25157         }
25158         
25159         if(store){
25160             this.refresh();
25161         }
25162     },
25163     /**
25164      * onbeforeLoad - masks the loading area.
25165      *
25166      */
25167     onBeforeLoad : function(store,opts)
25168     {
25169          Roo.log('onBeforeLoad');   
25170         if (!opts.add) {
25171             this.el.update("");
25172         }
25173         this.el.mask(this.mask ? this.mask : "Loading" ); 
25174     },
25175     onLoad : function ()
25176     {
25177         this.el.unmask();
25178     },
25179     
25180
25181     /**
25182      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25183      * @param {HTMLElement} node
25184      * @return {HTMLElement} The template node
25185      */
25186     findItemFromChild : function(node){
25187         var el = this.dataName  ?
25188             this.el.child('.roo-tpl-' + this.dataName,true) :
25189             this.el.dom; 
25190         
25191         if(!node || node.parentNode == el){
25192                     return node;
25193             }
25194             var p = node.parentNode;
25195             while(p && p != el){
25196             if(p.parentNode == el){
25197                 return p;
25198             }
25199             p = p.parentNode;
25200         }
25201             return null;
25202     },
25203
25204     /** @ignore */
25205     onClick : function(e){
25206         var item = this.findItemFromChild(e.getTarget());
25207         if(item){
25208             var index = this.indexOf(item);
25209             if(this.onItemClick(item, index, e) !== false){
25210                 this.fireEvent("click", this, index, item, e);
25211             }
25212         }else{
25213             this.clearSelections();
25214         }
25215     },
25216
25217     /** @ignore */
25218     onContextMenu : function(e){
25219         var item = this.findItemFromChild(e.getTarget());
25220         if(item){
25221             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25222         }
25223     },
25224
25225     /** @ignore */
25226     onDblClick : function(e){
25227         var item = this.findItemFromChild(e.getTarget());
25228         if(item){
25229             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25230         }
25231     },
25232
25233     onItemClick : function(item, index, e)
25234     {
25235         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25236             return false;
25237         }
25238         if (this.toggleSelect) {
25239             var m = this.isSelected(item) ? 'unselect' : 'select';
25240             Roo.log(m);
25241             var _t = this;
25242             _t[m](item, true, false);
25243             return true;
25244         }
25245         if(this.multiSelect || this.singleSelect){
25246             if(this.multiSelect && e.shiftKey && this.lastSelection){
25247                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25248             }else{
25249                 this.select(item, this.multiSelect && e.ctrlKey);
25250                 this.lastSelection = item;
25251             }
25252             e.preventDefault();
25253         }
25254         return true;
25255     },
25256
25257     /**
25258      * Get the number of selected nodes.
25259      * @return {Number}
25260      */
25261     getSelectionCount : function(){
25262         return this.selections.length;
25263     },
25264
25265     /**
25266      * Get the currently selected nodes.
25267      * @return {Array} An array of HTMLElements
25268      */
25269     getSelectedNodes : function(){
25270         return this.selections;
25271     },
25272
25273     /**
25274      * Get the indexes of the selected nodes.
25275      * @return {Array}
25276      */
25277     getSelectedIndexes : function(){
25278         var indexes = [], s = this.selections;
25279         for(var i = 0, len = s.length; i < len; i++){
25280             indexes.push(s[i].nodeIndex);
25281         }
25282         return indexes;
25283     },
25284
25285     /**
25286      * Clear all selections
25287      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25288      */
25289     clearSelections : function(suppressEvent){
25290         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25291             this.cmp.elements = this.selections;
25292             this.cmp.removeClass(this.selectedClass);
25293             this.selections = [];
25294             if(!suppressEvent){
25295                 this.fireEvent("selectionchange", this, this.selections);
25296             }
25297         }
25298     },
25299
25300     /**
25301      * Returns true if the passed node is selected
25302      * @param {HTMLElement/Number} node The node or node index
25303      * @return {Boolean}
25304      */
25305     isSelected : function(node){
25306         var s = this.selections;
25307         if(s.length < 1){
25308             return false;
25309         }
25310         node = this.getNode(node);
25311         return s.indexOf(node) !== -1;
25312     },
25313
25314     /**
25315      * Selects nodes.
25316      * @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
25317      * @param {Boolean} keepExisting (optional) true to keep existing selections
25318      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25319      */
25320     select : function(nodeInfo, keepExisting, suppressEvent){
25321         if(nodeInfo instanceof Array){
25322             if(!keepExisting){
25323                 this.clearSelections(true);
25324             }
25325             for(var i = 0, len = nodeInfo.length; i < len; i++){
25326                 this.select(nodeInfo[i], true, true);
25327             }
25328             return;
25329         } 
25330         var node = this.getNode(nodeInfo);
25331         if(!node || this.isSelected(node)){
25332             return; // already selected.
25333         }
25334         if(!keepExisting){
25335             this.clearSelections(true);
25336         }
25337         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25338             Roo.fly(node).addClass(this.selectedClass);
25339             this.selections.push(node);
25340             if(!suppressEvent){
25341                 this.fireEvent("selectionchange", this, this.selections);
25342             }
25343         }
25344         
25345         
25346     },
25347       /**
25348      * Unselects nodes.
25349      * @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
25350      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25351      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25352      */
25353     unselect : function(nodeInfo, keepExisting, suppressEvent)
25354     {
25355         if(nodeInfo instanceof Array){
25356             Roo.each(this.selections, function(s) {
25357                 this.unselect(s, nodeInfo);
25358             }, this);
25359             return;
25360         }
25361         var node = this.getNode(nodeInfo);
25362         if(!node || !this.isSelected(node)){
25363             Roo.log("not selected");
25364             return; // not selected.
25365         }
25366         // fireevent???
25367         var ns = [];
25368         Roo.each(this.selections, function(s) {
25369             if (s == node ) {
25370                 Roo.fly(node).removeClass(this.selectedClass);
25371
25372                 return;
25373             }
25374             ns.push(s);
25375         },this);
25376         
25377         this.selections= ns;
25378         this.fireEvent("selectionchange", this, this.selections);
25379     },
25380
25381     /**
25382      * Gets a template node.
25383      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25384      * @return {HTMLElement} The node or null if it wasn't found
25385      */
25386     getNode : function(nodeInfo){
25387         if(typeof nodeInfo == "string"){
25388             return document.getElementById(nodeInfo);
25389         }else if(typeof nodeInfo == "number"){
25390             return this.nodes[nodeInfo];
25391         }
25392         return nodeInfo;
25393     },
25394
25395     /**
25396      * Gets a range template nodes.
25397      * @param {Number} startIndex
25398      * @param {Number} endIndex
25399      * @return {Array} An array of nodes
25400      */
25401     getNodes : function(start, end){
25402         var ns = this.nodes;
25403         start = start || 0;
25404         end = typeof end == "undefined" ? ns.length - 1 : end;
25405         var nodes = [];
25406         if(start <= end){
25407             for(var i = start; i <= end; i++){
25408                 nodes.push(ns[i]);
25409             }
25410         } else{
25411             for(var i = start; i >= end; i--){
25412                 nodes.push(ns[i]);
25413             }
25414         }
25415         return nodes;
25416     },
25417
25418     /**
25419      * Finds the index of the passed node
25420      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25421      * @return {Number} The index of the node or -1
25422      */
25423     indexOf : function(node){
25424         node = this.getNode(node);
25425         if(typeof node.nodeIndex == "number"){
25426             return node.nodeIndex;
25427         }
25428         var ns = this.nodes;
25429         for(var i = 0, len = ns.length; i < len; i++){
25430             if(ns[i] == node){
25431                 return i;
25432             }
25433         }
25434         return -1;
25435     }
25436 });
25437 /*
25438  * Based on:
25439  * Ext JS Library 1.1.1
25440  * Copyright(c) 2006-2007, Ext JS, LLC.
25441  *
25442  * Originally Released Under LGPL - original licence link has changed is not relivant.
25443  *
25444  * Fork - LGPL
25445  * <script type="text/javascript">
25446  */
25447
25448 /**
25449  * @class Roo.JsonView
25450  * @extends Roo.View
25451  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25452 <pre><code>
25453 var view = new Roo.JsonView({
25454     container: "my-element",
25455     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25456     multiSelect: true, 
25457     jsonRoot: "data" 
25458 });
25459
25460 // listen for node click?
25461 view.on("click", function(vw, index, node, e){
25462     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25463 });
25464
25465 // direct load of JSON data
25466 view.load("foobar.php");
25467
25468 // Example from my blog list
25469 var tpl = new Roo.Template(
25470     '&lt;div class="entry"&gt;' +
25471     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25472     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25473     "&lt;/div&gt;&lt;hr /&gt;"
25474 );
25475
25476 var moreView = new Roo.JsonView({
25477     container :  "entry-list", 
25478     template : tpl,
25479     jsonRoot: "posts"
25480 });
25481 moreView.on("beforerender", this.sortEntries, this);
25482 moreView.load({
25483     url: "/blog/get-posts.php",
25484     params: "allposts=true",
25485     text: "Loading Blog Entries..."
25486 });
25487 </code></pre>
25488
25489 * Note: old code is supported with arguments : (container, template, config)
25490
25491
25492  * @constructor
25493  * Create a new JsonView
25494  * 
25495  * @param {Object} config The config object
25496  * 
25497  */
25498 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25499     
25500     
25501     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25502
25503     var um = this.el.getUpdateManager();
25504     um.setRenderer(this);
25505     um.on("update", this.onLoad, this);
25506     um.on("failure", this.onLoadException, this);
25507
25508     /**
25509      * @event beforerender
25510      * Fires before rendering of the downloaded JSON data.
25511      * @param {Roo.JsonView} this
25512      * @param {Object} data The JSON data loaded
25513      */
25514     /**
25515      * @event load
25516      * Fires when data is loaded.
25517      * @param {Roo.JsonView} this
25518      * @param {Object} data The JSON data loaded
25519      * @param {Object} response The raw Connect response object
25520      */
25521     /**
25522      * @event loadexception
25523      * Fires when loading fails.
25524      * @param {Roo.JsonView} this
25525      * @param {Object} response The raw Connect response object
25526      */
25527     this.addEvents({
25528         'beforerender' : true,
25529         'load' : true,
25530         'loadexception' : true
25531     });
25532 };
25533 Roo.extend(Roo.JsonView, Roo.View, {
25534     /**
25535      * @type {String} The root property in the loaded JSON object that contains the data
25536      */
25537     jsonRoot : "",
25538
25539     /**
25540      * Refreshes the view.
25541      */
25542     refresh : function(){
25543         this.clearSelections();
25544         this.el.update("");
25545         var html = [];
25546         var o = this.jsonData;
25547         if(o && o.length > 0){
25548             for(var i = 0, len = o.length; i < len; i++){
25549                 var data = this.prepareData(o[i], i, o);
25550                 html[html.length] = this.tpl.apply(data);
25551             }
25552         }else{
25553             html.push(this.emptyText);
25554         }
25555         this.el.update(html.join(""));
25556         this.nodes = this.el.dom.childNodes;
25557         this.updateIndexes(0);
25558     },
25559
25560     /**
25561      * 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.
25562      * @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:
25563      <pre><code>
25564      view.load({
25565          url: "your-url.php",
25566          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25567          callback: yourFunction,
25568          scope: yourObject, //(optional scope)
25569          discardUrl: false,
25570          nocache: false,
25571          text: "Loading...",
25572          timeout: 30,
25573          scripts: false
25574      });
25575      </code></pre>
25576      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25577      * 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.
25578      * @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}
25579      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25580      * @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.
25581      */
25582     load : function(){
25583         var um = this.el.getUpdateManager();
25584         um.update.apply(um, arguments);
25585     },
25586
25587     render : function(el, response){
25588         this.clearSelections();
25589         this.el.update("");
25590         var o;
25591         try{
25592             o = Roo.util.JSON.decode(response.responseText);
25593             if(this.jsonRoot){
25594                 
25595                 o = o[this.jsonRoot];
25596             }
25597         } catch(e){
25598         }
25599         /**
25600          * The current JSON data or null
25601          */
25602         this.jsonData = o;
25603         this.beforeRender();
25604         this.refresh();
25605     },
25606
25607 /**
25608  * Get the number of records in the current JSON dataset
25609  * @return {Number}
25610  */
25611     getCount : function(){
25612         return this.jsonData ? this.jsonData.length : 0;
25613     },
25614
25615 /**
25616  * Returns the JSON object for the specified node(s)
25617  * @param {HTMLElement/Array} node The node or an array of nodes
25618  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25619  * you get the JSON object for the node
25620  */
25621     getNodeData : function(node){
25622         if(node instanceof Array){
25623             var data = [];
25624             for(var i = 0, len = node.length; i < len; i++){
25625                 data.push(this.getNodeData(node[i]));
25626             }
25627             return data;
25628         }
25629         return this.jsonData[this.indexOf(node)] || null;
25630     },
25631
25632     beforeRender : function(){
25633         this.snapshot = this.jsonData;
25634         if(this.sortInfo){
25635             this.sort.apply(this, this.sortInfo);
25636         }
25637         this.fireEvent("beforerender", this, this.jsonData);
25638     },
25639
25640     onLoad : function(el, o){
25641         this.fireEvent("load", this, this.jsonData, o);
25642     },
25643
25644     onLoadException : function(el, o){
25645         this.fireEvent("loadexception", this, o);
25646     },
25647
25648 /**
25649  * Filter the data by a specific property.
25650  * @param {String} property A property on your JSON objects
25651  * @param {String/RegExp} value Either string that the property values
25652  * should start with, or a RegExp to test against the property
25653  */
25654     filter : function(property, value){
25655         if(this.jsonData){
25656             var data = [];
25657             var ss = this.snapshot;
25658             if(typeof value == "string"){
25659                 var vlen = value.length;
25660                 if(vlen == 0){
25661                     this.clearFilter();
25662                     return;
25663                 }
25664                 value = value.toLowerCase();
25665                 for(var i = 0, len = ss.length; i < len; i++){
25666                     var o = ss[i];
25667                     if(o[property].substr(0, vlen).toLowerCase() == value){
25668                         data.push(o);
25669                     }
25670                 }
25671             } else if(value.exec){ // regex?
25672                 for(var i = 0, len = ss.length; i < len; i++){
25673                     var o = ss[i];
25674                     if(value.test(o[property])){
25675                         data.push(o);
25676                     }
25677                 }
25678             } else{
25679                 return;
25680             }
25681             this.jsonData = data;
25682             this.refresh();
25683         }
25684     },
25685
25686 /**
25687  * Filter by a function. The passed function will be called with each
25688  * object in the current dataset. If the function returns true the value is kept,
25689  * otherwise it is filtered.
25690  * @param {Function} fn
25691  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25692  */
25693     filterBy : function(fn, scope){
25694         if(this.jsonData){
25695             var data = [];
25696             var ss = this.snapshot;
25697             for(var i = 0, len = ss.length; i < len; i++){
25698                 var o = ss[i];
25699                 if(fn.call(scope || this, o)){
25700                     data.push(o);
25701                 }
25702             }
25703             this.jsonData = data;
25704             this.refresh();
25705         }
25706     },
25707
25708 /**
25709  * Clears the current filter.
25710  */
25711     clearFilter : function(){
25712         if(this.snapshot && this.jsonData != this.snapshot){
25713             this.jsonData = this.snapshot;
25714             this.refresh();
25715         }
25716     },
25717
25718
25719 /**
25720  * Sorts the data for this view and refreshes it.
25721  * @param {String} property A property on your JSON objects to sort on
25722  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25723  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25724  */
25725     sort : function(property, dir, sortType){
25726         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25727         if(this.jsonData){
25728             var p = property;
25729             var dsc = dir && dir.toLowerCase() == "desc";
25730             var f = function(o1, o2){
25731                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25732                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25733                 ;
25734                 if(v1 < v2){
25735                     return dsc ? +1 : -1;
25736                 } else if(v1 > v2){
25737                     return dsc ? -1 : +1;
25738                 } else{
25739                     return 0;
25740                 }
25741             };
25742             this.jsonData.sort(f);
25743             this.refresh();
25744             if(this.jsonData != this.snapshot){
25745                 this.snapshot.sort(f);
25746             }
25747         }
25748     }
25749 });/*
25750  * Based on:
25751  * Ext JS Library 1.1.1
25752  * Copyright(c) 2006-2007, Ext JS, LLC.
25753  *
25754  * Originally Released Under LGPL - original licence link has changed is not relivant.
25755  *
25756  * Fork - LGPL
25757  * <script type="text/javascript">
25758  */
25759  
25760
25761 /**
25762  * @class Roo.ColorPalette
25763  * @extends Roo.Component
25764  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25765  * Here's an example of typical usage:
25766  * <pre><code>
25767 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25768 cp.render('my-div');
25769
25770 cp.on('select', function(palette, selColor){
25771     // do something with selColor
25772 });
25773 </code></pre>
25774  * @constructor
25775  * Create a new ColorPalette
25776  * @param {Object} config The config object
25777  */
25778 Roo.ColorPalette = function(config){
25779     Roo.ColorPalette.superclass.constructor.call(this, config);
25780     this.addEvents({
25781         /**
25782              * @event select
25783              * Fires when a color is selected
25784              * @param {ColorPalette} this
25785              * @param {String} color The 6-digit color hex code (without the # symbol)
25786              */
25787         select: true
25788     });
25789
25790     if(this.handler){
25791         this.on("select", this.handler, this.scope, true);
25792     }
25793 };
25794 Roo.extend(Roo.ColorPalette, Roo.Component, {
25795     /**
25796      * @cfg {String} itemCls
25797      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25798      */
25799     itemCls : "x-color-palette",
25800     /**
25801      * @cfg {String} value
25802      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25803      * the hex codes are case-sensitive.
25804      */
25805     value : null,
25806     clickEvent:'click',
25807     // private
25808     ctype: "Roo.ColorPalette",
25809
25810     /**
25811      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25812      */
25813     allowReselect : false,
25814
25815     /**
25816      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25817      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25818      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25819      * of colors with the width setting until the box is symmetrical.</p>
25820      * <p>You can override individual colors if needed:</p>
25821      * <pre><code>
25822 var cp = new Roo.ColorPalette();
25823 cp.colors[0] = "FF0000";  // change the first box to red
25824 </code></pre>
25825
25826 Or you can provide a custom array of your own for complete control:
25827 <pre><code>
25828 var cp = new Roo.ColorPalette();
25829 cp.colors = ["000000", "993300", "333300"];
25830 </code></pre>
25831      * @type Array
25832      */
25833     colors : [
25834         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25835         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25836         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25837         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25838         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25839     ],
25840
25841     // private
25842     onRender : function(container, position){
25843         var t = new Roo.MasterTemplate(
25844             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25845         );
25846         var c = this.colors;
25847         for(var i = 0, len = c.length; i < len; i++){
25848             t.add([c[i]]);
25849         }
25850         var el = document.createElement("div");
25851         el.className = this.itemCls;
25852         t.overwrite(el);
25853         container.dom.insertBefore(el, position);
25854         this.el = Roo.get(el);
25855         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25856         if(this.clickEvent != 'click'){
25857             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25858         }
25859     },
25860
25861     // private
25862     afterRender : function(){
25863         Roo.ColorPalette.superclass.afterRender.call(this);
25864         if(this.value){
25865             var s = this.value;
25866             this.value = null;
25867             this.select(s);
25868         }
25869     },
25870
25871     // private
25872     handleClick : function(e, t){
25873         e.preventDefault();
25874         if(!this.disabled){
25875             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25876             this.select(c.toUpperCase());
25877         }
25878     },
25879
25880     /**
25881      * Selects the specified color in the palette (fires the select event)
25882      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25883      */
25884     select : function(color){
25885         color = color.replace("#", "");
25886         if(color != this.value || this.allowReselect){
25887             var el = this.el;
25888             if(this.value){
25889                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25890             }
25891             el.child("a.color-"+color).addClass("x-color-palette-sel");
25892             this.value = color;
25893             this.fireEvent("select", this, color);
25894         }
25895     }
25896 });/*
25897  * Based on:
25898  * Ext JS Library 1.1.1
25899  * Copyright(c) 2006-2007, Ext JS, LLC.
25900  *
25901  * Originally Released Under LGPL - original licence link has changed is not relivant.
25902  *
25903  * Fork - LGPL
25904  * <script type="text/javascript">
25905  */
25906  
25907 /**
25908  * @class Roo.DatePicker
25909  * @extends Roo.Component
25910  * Simple date picker class.
25911  * @constructor
25912  * Create a new DatePicker
25913  * @param {Object} config The config object
25914  */
25915 Roo.DatePicker = function(config){
25916     Roo.DatePicker.superclass.constructor.call(this, config);
25917
25918     this.value = config && config.value ?
25919                  config.value.clearTime() : new Date().clearTime();
25920
25921     this.addEvents({
25922         /**
25923              * @event select
25924              * Fires when a date is selected
25925              * @param {DatePicker} this
25926              * @param {Date} date The selected date
25927              */
25928         'select': true,
25929         /**
25930              * @event monthchange
25931              * Fires when the displayed month changes 
25932              * @param {DatePicker} this
25933              * @param {Date} date The selected month
25934              */
25935         'monthchange': true
25936     });
25937
25938     if(this.handler){
25939         this.on("select", this.handler,  this.scope || this);
25940     }
25941     // build the disabledDatesRE
25942     if(!this.disabledDatesRE && this.disabledDates){
25943         var dd = this.disabledDates;
25944         var re = "(?:";
25945         for(var i = 0; i < dd.length; i++){
25946             re += dd[i];
25947             if(i != dd.length-1) re += "|";
25948         }
25949         this.disabledDatesRE = new RegExp(re + ")");
25950     }
25951 };
25952
25953 Roo.extend(Roo.DatePicker, Roo.Component, {
25954     /**
25955      * @cfg {String} todayText
25956      * The text to display on the button that selects the current date (defaults to "Today")
25957      */
25958     todayText : "Today",
25959     /**
25960      * @cfg {String} okText
25961      * The text to display on the ok button
25962      */
25963     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25964     /**
25965      * @cfg {String} cancelText
25966      * The text to display on the cancel button
25967      */
25968     cancelText : "Cancel",
25969     /**
25970      * @cfg {String} todayTip
25971      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25972      */
25973     todayTip : "{0} (Spacebar)",
25974     /**
25975      * @cfg {Date} minDate
25976      * Minimum allowable date (JavaScript date object, defaults to null)
25977      */
25978     minDate : null,
25979     /**
25980      * @cfg {Date} maxDate
25981      * Maximum allowable date (JavaScript date object, defaults to null)
25982      */
25983     maxDate : null,
25984     /**
25985      * @cfg {String} minText
25986      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25987      */
25988     minText : "This date is before the minimum date",
25989     /**
25990      * @cfg {String} maxText
25991      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25992      */
25993     maxText : "This date is after the maximum date",
25994     /**
25995      * @cfg {String} format
25996      * The default date format string which can be overriden for localization support.  The format must be
25997      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25998      */
25999     format : "m/d/y",
26000     /**
26001      * @cfg {Array} disabledDays
26002      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26003      */
26004     disabledDays : null,
26005     /**
26006      * @cfg {String} disabledDaysText
26007      * The tooltip to display when the date falls on a disabled day (defaults to "")
26008      */
26009     disabledDaysText : "",
26010     /**
26011      * @cfg {RegExp} disabledDatesRE
26012      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26013      */
26014     disabledDatesRE : null,
26015     /**
26016      * @cfg {String} disabledDatesText
26017      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26018      */
26019     disabledDatesText : "",
26020     /**
26021      * @cfg {Boolean} constrainToViewport
26022      * True to constrain the date picker to the viewport (defaults to true)
26023      */
26024     constrainToViewport : true,
26025     /**
26026      * @cfg {Array} monthNames
26027      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26028      */
26029     monthNames : Date.monthNames,
26030     /**
26031      * @cfg {Array} dayNames
26032      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26033      */
26034     dayNames : Date.dayNames,
26035     /**
26036      * @cfg {String} nextText
26037      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26038      */
26039     nextText: 'Next Month (Control+Right)',
26040     /**
26041      * @cfg {String} prevText
26042      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26043      */
26044     prevText: 'Previous Month (Control+Left)',
26045     /**
26046      * @cfg {String} monthYearText
26047      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26048      */
26049     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26050     /**
26051      * @cfg {Number} startDay
26052      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26053      */
26054     startDay : 0,
26055     /**
26056      * @cfg {Bool} showClear
26057      * Show a clear button (usefull for date form elements that can be blank.)
26058      */
26059     
26060     showClear: false,
26061     
26062     /**
26063      * Sets the value of the date field
26064      * @param {Date} value The date to set
26065      */
26066     setValue : function(value){
26067         var old = this.value;
26068         
26069         if (typeof(value) == 'string') {
26070          
26071             value = Date.parseDate(value, this.format);
26072         }
26073         if (!value) {
26074             value = new Date();
26075         }
26076         
26077         this.value = value.clearTime(true);
26078         if(this.el){
26079             this.update(this.value);
26080         }
26081     },
26082
26083     /**
26084      * Gets the current selected value of the date field
26085      * @return {Date} The selected date
26086      */
26087     getValue : function(){
26088         return this.value;
26089     },
26090
26091     // private
26092     focus : function(){
26093         if(this.el){
26094             this.update(this.activeDate);
26095         }
26096     },
26097
26098     // privateval
26099     onRender : function(container, position){
26100         
26101         var m = [
26102              '<table cellspacing="0">',
26103                 '<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>',
26104                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26105         var dn = this.dayNames;
26106         for(var i = 0; i < 7; i++){
26107             var d = this.startDay+i;
26108             if(d > 6){
26109                 d = d-7;
26110             }
26111             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26112         }
26113         m[m.length] = "</tr></thead><tbody><tr>";
26114         for(var i = 0; i < 42; i++) {
26115             if(i % 7 == 0 && i != 0){
26116                 m[m.length] = "</tr><tr>";
26117             }
26118             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26119         }
26120         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26121             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26122
26123         var el = document.createElement("div");
26124         el.className = "x-date-picker";
26125         el.innerHTML = m.join("");
26126
26127         container.dom.insertBefore(el, position);
26128
26129         this.el = Roo.get(el);
26130         this.eventEl = Roo.get(el.firstChild);
26131
26132         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26133             handler: this.showPrevMonth,
26134             scope: this,
26135             preventDefault:true,
26136             stopDefault:true
26137         });
26138
26139         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26140             handler: this.showNextMonth,
26141             scope: this,
26142             preventDefault:true,
26143             stopDefault:true
26144         });
26145
26146         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26147
26148         this.monthPicker = this.el.down('div.x-date-mp');
26149         this.monthPicker.enableDisplayMode('block');
26150         
26151         var kn = new Roo.KeyNav(this.eventEl, {
26152             "left" : function(e){
26153                 e.ctrlKey ?
26154                     this.showPrevMonth() :
26155                     this.update(this.activeDate.add("d", -1));
26156             },
26157
26158             "right" : function(e){
26159                 e.ctrlKey ?
26160                     this.showNextMonth() :
26161                     this.update(this.activeDate.add("d", 1));
26162             },
26163
26164             "up" : function(e){
26165                 e.ctrlKey ?
26166                     this.showNextYear() :
26167                     this.update(this.activeDate.add("d", -7));
26168             },
26169
26170             "down" : function(e){
26171                 e.ctrlKey ?
26172                     this.showPrevYear() :
26173                     this.update(this.activeDate.add("d", 7));
26174             },
26175
26176             "pageUp" : function(e){
26177                 this.showNextMonth();
26178             },
26179
26180             "pageDown" : function(e){
26181                 this.showPrevMonth();
26182             },
26183
26184             "enter" : function(e){
26185                 e.stopPropagation();
26186                 return true;
26187             },
26188
26189             scope : this
26190         });
26191
26192         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26193
26194         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26195
26196         this.el.unselectable();
26197         
26198         this.cells = this.el.select("table.x-date-inner tbody td");
26199         this.textNodes = this.el.query("table.x-date-inner tbody span");
26200
26201         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26202             text: "&#160;",
26203             tooltip: this.monthYearText
26204         });
26205
26206         this.mbtn.on('click', this.showMonthPicker, this);
26207         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26208
26209
26210         var today = (new Date()).dateFormat(this.format);
26211         
26212         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26213         if (this.showClear) {
26214             baseTb.add( new Roo.Toolbar.Fill());
26215         }
26216         baseTb.add({
26217             text: String.format(this.todayText, today),
26218             tooltip: String.format(this.todayTip, today),
26219             handler: this.selectToday,
26220             scope: this
26221         });
26222         
26223         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26224             
26225         //});
26226         if (this.showClear) {
26227             
26228             baseTb.add( new Roo.Toolbar.Fill());
26229             baseTb.add({
26230                 text: '&#160;',
26231                 cls: 'x-btn-icon x-btn-clear',
26232                 handler: function() {
26233                     //this.value = '';
26234                     this.fireEvent("select", this, '');
26235                 },
26236                 scope: this
26237             });
26238         }
26239         
26240         
26241         if(Roo.isIE){
26242             this.el.repaint();
26243         }
26244         this.update(this.value);
26245     },
26246
26247     createMonthPicker : function(){
26248         if(!this.monthPicker.dom.firstChild){
26249             var buf = ['<table border="0" cellspacing="0">'];
26250             for(var i = 0; i < 6; i++){
26251                 buf.push(
26252                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26253                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26254                     i == 0 ?
26255                     '<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>' :
26256                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26257                 );
26258             }
26259             buf.push(
26260                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26261                     this.okText,
26262                     '</button><button type="button" class="x-date-mp-cancel">',
26263                     this.cancelText,
26264                     '</button></td></tr>',
26265                 '</table>'
26266             );
26267             this.monthPicker.update(buf.join(''));
26268             this.monthPicker.on('click', this.onMonthClick, this);
26269             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26270
26271             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26272             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26273
26274             this.mpMonths.each(function(m, a, i){
26275                 i += 1;
26276                 if((i%2) == 0){
26277                     m.dom.xmonth = 5 + Math.round(i * .5);
26278                 }else{
26279                     m.dom.xmonth = Math.round((i-1) * .5);
26280                 }
26281             });
26282         }
26283     },
26284
26285     showMonthPicker : function(){
26286         this.createMonthPicker();
26287         var size = this.el.getSize();
26288         this.monthPicker.setSize(size);
26289         this.monthPicker.child('table').setSize(size);
26290
26291         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26292         this.updateMPMonth(this.mpSelMonth);
26293         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26294         this.updateMPYear(this.mpSelYear);
26295
26296         this.monthPicker.slideIn('t', {duration:.2});
26297     },
26298
26299     updateMPYear : function(y){
26300         this.mpyear = y;
26301         var ys = this.mpYears.elements;
26302         for(var i = 1; i <= 10; i++){
26303             var td = ys[i-1], y2;
26304             if((i%2) == 0){
26305                 y2 = y + Math.round(i * .5);
26306                 td.firstChild.innerHTML = y2;
26307                 td.xyear = y2;
26308             }else{
26309                 y2 = y - (5-Math.round(i * .5));
26310                 td.firstChild.innerHTML = y2;
26311                 td.xyear = y2;
26312             }
26313             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26314         }
26315     },
26316
26317     updateMPMonth : function(sm){
26318         this.mpMonths.each(function(m, a, i){
26319             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26320         });
26321     },
26322
26323     selectMPMonth: function(m){
26324         
26325     },
26326
26327     onMonthClick : function(e, t){
26328         e.stopEvent();
26329         var el = new Roo.Element(t), pn;
26330         if(el.is('button.x-date-mp-cancel')){
26331             this.hideMonthPicker();
26332         }
26333         else if(el.is('button.x-date-mp-ok')){
26334             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26335             this.hideMonthPicker();
26336         }
26337         else if(pn = el.up('td.x-date-mp-month', 2)){
26338             this.mpMonths.removeClass('x-date-mp-sel');
26339             pn.addClass('x-date-mp-sel');
26340             this.mpSelMonth = pn.dom.xmonth;
26341         }
26342         else if(pn = el.up('td.x-date-mp-year', 2)){
26343             this.mpYears.removeClass('x-date-mp-sel');
26344             pn.addClass('x-date-mp-sel');
26345             this.mpSelYear = pn.dom.xyear;
26346         }
26347         else if(el.is('a.x-date-mp-prev')){
26348             this.updateMPYear(this.mpyear-10);
26349         }
26350         else if(el.is('a.x-date-mp-next')){
26351             this.updateMPYear(this.mpyear+10);
26352         }
26353     },
26354
26355     onMonthDblClick : function(e, t){
26356         e.stopEvent();
26357         var el = new Roo.Element(t), pn;
26358         if(pn = el.up('td.x-date-mp-month', 2)){
26359             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26360             this.hideMonthPicker();
26361         }
26362         else if(pn = el.up('td.x-date-mp-year', 2)){
26363             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26364             this.hideMonthPicker();
26365         }
26366     },
26367
26368     hideMonthPicker : function(disableAnim){
26369         if(this.monthPicker){
26370             if(disableAnim === true){
26371                 this.monthPicker.hide();
26372             }else{
26373                 this.monthPicker.slideOut('t', {duration:.2});
26374             }
26375         }
26376     },
26377
26378     // private
26379     showPrevMonth : function(e){
26380         this.update(this.activeDate.add("mo", -1));
26381     },
26382
26383     // private
26384     showNextMonth : function(e){
26385         this.update(this.activeDate.add("mo", 1));
26386     },
26387
26388     // private
26389     showPrevYear : function(){
26390         this.update(this.activeDate.add("y", -1));
26391     },
26392
26393     // private
26394     showNextYear : function(){
26395         this.update(this.activeDate.add("y", 1));
26396     },
26397
26398     // private
26399     handleMouseWheel : function(e){
26400         var delta = e.getWheelDelta();
26401         if(delta > 0){
26402             this.showPrevMonth();
26403             e.stopEvent();
26404         } else if(delta < 0){
26405             this.showNextMonth();
26406             e.stopEvent();
26407         }
26408     },
26409
26410     // private
26411     handleDateClick : function(e, t){
26412         e.stopEvent();
26413         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26414             this.setValue(new Date(t.dateValue));
26415             this.fireEvent("select", this, this.value);
26416         }
26417     },
26418
26419     // private
26420     selectToday : function(){
26421         this.setValue(new Date().clearTime());
26422         this.fireEvent("select", this, this.value);
26423     },
26424
26425     // private
26426     update : function(date)
26427     {
26428         var vd = this.activeDate;
26429         this.activeDate = date;
26430         if(vd && this.el){
26431             var t = date.getTime();
26432             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26433                 this.cells.removeClass("x-date-selected");
26434                 this.cells.each(function(c){
26435                    if(c.dom.firstChild.dateValue == t){
26436                        c.addClass("x-date-selected");
26437                        setTimeout(function(){
26438                             try{c.dom.firstChild.focus();}catch(e){}
26439                        }, 50);
26440                        return false;
26441                    }
26442                 });
26443                 return;
26444             }
26445         }
26446         
26447         var days = date.getDaysInMonth();
26448         var firstOfMonth = date.getFirstDateOfMonth();
26449         var startingPos = firstOfMonth.getDay()-this.startDay;
26450
26451         if(startingPos <= this.startDay){
26452             startingPos += 7;
26453         }
26454
26455         var pm = date.add("mo", -1);
26456         var prevStart = pm.getDaysInMonth()-startingPos;
26457
26458         var cells = this.cells.elements;
26459         var textEls = this.textNodes;
26460         days += startingPos;
26461
26462         // convert everything to numbers so it's fast
26463         var day = 86400000;
26464         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26465         var today = new Date().clearTime().getTime();
26466         var sel = date.clearTime().getTime();
26467         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26468         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26469         var ddMatch = this.disabledDatesRE;
26470         var ddText = this.disabledDatesText;
26471         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26472         var ddaysText = this.disabledDaysText;
26473         var format = this.format;
26474
26475         var setCellClass = function(cal, cell){
26476             cell.title = "";
26477             var t = d.getTime();
26478             cell.firstChild.dateValue = t;
26479             if(t == today){
26480                 cell.className += " x-date-today";
26481                 cell.title = cal.todayText;
26482             }
26483             if(t == sel){
26484                 cell.className += " x-date-selected";
26485                 setTimeout(function(){
26486                     try{cell.firstChild.focus();}catch(e){}
26487                 }, 50);
26488             }
26489             // disabling
26490             if(t < min) {
26491                 cell.className = " x-date-disabled";
26492                 cell.title = cal.minText;
26493                 return;
26494             }
26495             if(t > max) {
26496                 cell.className = " x-date-disabled";
26497                 cell.title = cal.maxText;
26498                 return;
26499             }
26500             if(ddays){
26501                 if(ddays.indexOf(d.getDay()) != -1){
26502                     cell.title = ddaysText;
26503                     cell.className = " x-date-disabled";
26504                 }
26505             }
26506             if(ddMatch && format){
26507                 var fvalue = d.dateFormat(format);
26508                 if(ddMatch.test(fvalue)){
26509                     cell.title = ddText.replace("%0", fvalue);
26510                     cell.className = " x-date-disabled";
26511                 }
26512             }
26513         };
26514
26515         var i = 0;
26516         for(; i < startingPos; i++) {
26517             textEls[i].innerHTML = (++prevStart);
26518             d.setDate(d.getDate()+1);
26519             cells[i].className = "x-date-prevday";
26520             setCellClass(this, cells[i]);
26521         }
26522         for(; i < days; i++){
26523             intDay = i - startingPos + 1;
26524             textEls[i].innerHTML = (intDay);
26525             d.setDate(d.getDate()+1);
26526             cells[i].className = "x-date-active";
26527             setCellClass(this, cells[i]);
26528         }
26529         var extraDays = 0;
26530         for(; i < 42; i++) {
26531              textEls[i].innerHTML = (++extraDays);
26532              d.setDate(d.getDate()+1);
26533              cells[i].className = "x-date-nextday";
26534              setCellClass(this, cells[i]);
26535         }
26536
26537         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26538         this.fireEvent('monthchange', this, date);
26539         
26540         if(!this.internalRender){
26541             var main = this.el.dom.firstChild;
26542             var w = main.offsetWidth;
26543             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26544             Roo.fly(main).setWidth(w);
26545             this.internalRender = true;
26546             // opera does not respect the auto grow header center column
26547             // then, after it gets a width opera refuses to recalculate
26548             // without a second pass
26549             if(Roo.isOpera && !this.secondPass){
26550                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26551                 this.secondPass = true;
26552                 this.update.defer(10, this, [date]);
26553             }
26554         }
26555         
26556         
26557     }
26558 });        /*
26559  * Based on:
26560  * Ext JS Library 1.1.1
26561  * Copyright(c) 2006-2007, Ext JS, LLC.
26562  *
26563  * Originally Released Under LGPL - original licence link has changed is not relivant.
26564  *
26565  * Fork - LGPL
26566  * <script type="text/javascript">
26567  */
26568 /**
26569  * @class Roo.TabPanel
26570  * @extends Roo.util.Observable
26571  * A lightweight tab container.
26572  * <br><br>
26573  * Usage:
26574  * <pre><code>
26575 // basic tabs 1, built from existing content
26576 var tabs = new Roo.TabPanel("tabs1");
26577 tabs.addTab("script", "View Script");
26578 tabs.addTab("markup", "View Markup");
26579 tabs.activate("script");
26580
26581 // more advanced tabs, built from javascript
26582 var jtabs = new Roo.TabPanel("jtabs");
26583 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26584
26585 // set up the UpdateManager
26586 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26587 var updater = tab2.getUpdateManager();
26588 updater.setDefaultUrl("ajax1.htm");
26589 tab2.on('activate', updater.refresh, updater, true);
26590
26591 // Use setUrl for Ajax loading
26592 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26593 tab3.setUrl("ajax2.htm", null, true);
26594
26595 // Disabled tab
26596 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26597 tab4.disable();
26598
26599 jtabs.activate("jtabs-1");
26600  * </code></pre>
26601  * @constructor
26602  * Create a new TabPanel.
26603  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26604  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26605  */
26606 Roo.TabPanel = function(container, config){
26607     /**
26608     * The container element for this TabPanel.
26609     * @type Roo.Element
26610     */
26611     this.el = Roo.get(container, true);
26612     if(config){
26613         if(typeof config == "boolean"){
26614             this.tabPosition = config ? "bottom" : "top";
26615         }else{
26616             Roo.apply(this, config);
26617         }
26618     }
26619     if(this.tabPosition == "bottom"){
26620         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26621         this.el.addClass("x-tabs-bottom");
26622     }
26623     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26624     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26625     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26626     if(Roo.isIE){
26627         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26628     }
26629     if(this.tabPosition != "bottom"){
26630         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26631          * @type Roo.Element
26632          */
26633         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26634         this.el.addClass("x-tabs-top");
26635     }
26636     this.items = [];
26637
26638     this.bodyEl.setStyle("position", "relative");
26639
26640     this.active = null;
26641     this.activateDelegate = this.activate.createDelegate(this);
26642
26643     this.addEvents({
26644         /**
26645          * @event tabchange
26646          * Fires when the active tab changes
26647          * @param {Roo.TabPanel} this
26648          * @param {Roo.TabPanelItem} activePanel The new active tab
26649          */
26650         "tabchange": true,
26651         /**
26652          * @event beforetabchange
26653          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26654          * @param {Roo.TabPanel} this
26655          * @param {Object} e Set cancel to true on this object to cancel the tab change
26656          * @param {Roo.TabPanelItem} tab The tab being changed to
26657          */
26658         "beforetabchange" : true
26659     });
26660
26661     Roo.EventManager.onWindowResize(this.onResize, this);
26662     this.cpad = this.el.getPadding("lr");
26663     this.hiddenCount = 0;
26664
26665
26666     // toolbar on the tabbar support...
26667     if (this.toolbar) {
26668         var tcfg = this.toolbar;
26669         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26670         this.toolbar = new Roo.Toolbar(tcfg);
26671         if (Roo.isSafari) {
26672             var tbl = tcfg.container.child('table', true);
26673             tbl.setAttribute('width', '100%');
26674         }
26675         
26676     }
26677    
26678
26679
26680     Roo.TabPanel.superclass.constructor.call(this);
26681 };
26682
26683 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26684     /*
26685      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26686      */
26687     tabPosition : "top",
26688     /*
26689      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26690      */
26691     currentTabWidth : 0,
26692     /*
26693      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26694      */
26695     minTabWidth : 40,
26696     /*
26697      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26698      */
26699     maxTabWidth : 250,
26700     /*
26701      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26702      */
26703     preferredTabWidth : 175,
26704     /*
26705      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26706      */
26707     resizeTabs : false,
26708     /*
26709      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26710      */
26711     monitorResize : true,
26712     /*
26713      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26714      */
26715     toolbar : false,
26716
26717     /**
26718      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26719      * @param {String} id The id of the div to use <b>or create</b>
26720      * @param {String} text The text for the tab
26721      * @param {String} content (optional) Content to put in the TabPanelItem body
26722      * @param {Boolean} closable (optional) True to create a close icon on the tab
26723      * @return {Roo.TabPanelItem} The created TabPanelItem
26724      */
26725     addTab : function(id, text, content, closable){
26726         var item = new Roo.TabPanelItem(this, id, text, closable);
26727         this.addTabItem(item);
26728         if(content){
26729             item.setContent(content);
26730         }
26731         return item;
26732     },
26733
26734     /**
26735      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26736      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26737      * @return {Roo.TabPanelItem}
26738      */
26739     getTab : function(id){
26740         return this.items[id];
26741     },
26742
26743     /**
26744      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26745      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26746      */
26747     hideTab : function(id){
26748         var t = this.items[id];
26749         if(!t.isHidden()){
26750            t.setHidden(true);
26751            this.hiddenCount++;
26752            this.autoSizeTabs();
26753         }
26754     },
26755
26756     /**
26757      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26758      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26759      */
26760     unhideTab : function(id){
26761         var t = this.items[id];
26762         if(t.isHidden()){
26763            t.setHidden(false);
26764            this.hiddenCount--;
26765            this.autoSizeTabs();
26766         }
26767     },
26768
26769     /**
26770      * Adds an existing {@link Roo.TabPanelItem}.
26771      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26772      */
26773     addTabItem : function(item){
26774         this.items[item.id] = item;
26775         this.items.push(item);
26776         if(this.resizeTabs){
26777            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26778            this.autoSizeTabs();
26779         }else{
26780             item.autoSize();
26781         }
26782     },
26783
26784     /**
26785      * Removes a {@link Roo.TabPanelItem}.
26786      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26787      */
26788     removeTab : function(id){
26789         var items = this.items;
26790         var tab = items[id];
26791         if(!tab) { return; }
26792         var index = items.indexOf(tab);
26793         if(this.active == tab && items.length > 1){
26794             var newTab = this.getNextAvailable(index);
26795             if(newTab) {
26796                 newTab.activate();
26797             }
26798         }
26799         this.stripEl.dom.removeChild(tab.pnode.dom);
26800         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26801             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26802         }
26803         items.splice(index, 1);
26804         delete this.items[tab.id];
26805         tab.fireEvent("close", tab);
26806         tab.purgeListeners();
26807         this.autoSizeTabs();
26808     },
26809
26810     getNextAvailable : function(start){
26811         var items = this.items;
26812         var index = start;
26813         // look for a next tab that will slide over to
26814         // replace the one being removed
26815         while(index < items.length){
26816             var item = items[++index];
26817             if(item && !item.isHidden()){
26818                 return item;
26819             }
26820         }
26821         // if one isn't found select the previous tab (on the left)
26822         index = start;
26823         while(index >= 0){
26824             var item = items[--index];
26825             if(item && !item.isHidden()){
26826                 return item;
26827             }
26828         }
26829         return null;
26830     },
26831
26832     /**
26833      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26834      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26835      */
26836     disableTab : function(id){
26837         var tab = this.items[id];
26838         if(tab && this.active != tab){
26839             tab.disable();
26840         }
26841     },
26842
26843     /**
26844      * Enables a {@link Roo.TabPanelItem} that is disabled.
26845      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26846      */
26847     enableTab : function(id){
26848         var tab = this.items[id];
26849         tab.enable();
26850     },
26851
26852     /**
26853      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26854      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26855      * @return {Roo.TabPanelItem} The TabPanelItem.
26856      */
26857     activate : function(id){
26858         var tab = this.items[id];
26859         if(!tab){
26860             return null;
26861         }
26862         if(tab == this.active || tab.disabled){
26863             return tab;
26864         }
26865         var e = {};
26866         this.fireEvent("beforetabchange", this, e, tab);
26867         if(e.cancel !== true && !tab.disabled){
26868             if(this.active){
26869                 this.active.hide();
26870             }
26871             this.active = this.items[id];
26872             this.active.show();
26873             this.fireEvent("tabchange", this, this.active);
26874         }
26875         return tab;
26876     },
26877
26878     /**
26879      * Gets the active {@link Roo.TabPanelItem}.
26880      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26881      */
26882     getActiveTab : function(){
26883         return this.active;
26884     },
26885
26886     /**
26887      * Updates the tab body element to fit the height of the container element
26888      * for overflow scrolling
26889      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26890      */
26891     syncHeight : function(targetHeight){
26892         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26893         var bm = this.bodyEl.getMargins();
26894         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26895         this.bodyEl.setHeight(newHeight);
26896         return newHeight;
26897     },
26898
26899     onResize : function(){
26900         if(this.monitorResize){
26901             this.autoSizeTabs();
26902         }
26903     },
26904
26905     /**
26906      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26907      */
26908     beginUpdate : function(){
26909         this.updating = true;
26910     },
26911
26912     /**
26913      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26914      */
26915     endUpdate : function(){
26916         this.updating = false;
26917         this.autoSizeTabs();
26918     },
26919
26920     /**
26921      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26922      */
26923     autoSizeTabs : function(){
26924         var count = this.items.length;
26925         var vcount = count - this.hiddenCount;
26926         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26927         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26928         var availWidth = Math.floor(w / vcount);
26929         var b = this.stripBody;
26930         if(b.getWidth() > w){
26931             var tabs = this.items;
26932             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26933             if(availWidth < this.minTabWidth){
26934                 /*if(!this.sleft){    // incomplete scrolling code
26935                     this.createScrollButtons();
26936                 }
26937                 this.showScroll();
26938                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26939             }
26940         }else{
26941             if(this.currentTabWidth < this.preferredTabWidth){
26942                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26943             }
26944         }
26945     },
26946
26947     /**
26948      * Returns the number of tabs in this TabPanel.
26949      * @return {Number}
26950      */
26951      getCount : function(){
26952          return this.items.length;
26953      },
26954
26955     /**
26956      * Resizes all the tabs to the passed width
26957      * @param {Number} The new width
26958      */
26959     setTabWidth : function(width){
26960         this.currentTabWidth = width;
26961         for(var i = 0, len = this.items.length; i < len; i++) {
26962                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26963         }
26964     },
26965
26966     /**
26967      * Destroys this TabPanel
26968      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26969      */
26970     destroy : function(removeEl){
26971         Roo.EventManager.removeResizeListener(this.onResize, this);
26972         for(var i = 0, len = this.items.length; i < len; i++){
26973             this.items[i].purgeListeners();
26974         }
26975         if(removeEl === true){
26976             this.el.update("");
26977             this.el.remove();
26978         }
26979     }
26980 });
26981
26982 /**
26983  * @class Roo.TabPanelItem
26984  * @extends Roo.util.Observable
26985  * Represents an individual item (tab plus body) in a TabPanel.
26986  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26987  * @param {String} id The id of this TabPanelItem
26988  * @param {String} text The text for the tab of this TabPanelItem
26989  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26990  */
26991 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26992     /**
26993      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26994      * @type Roo.TabPanel
26995      */
26996     this.tabPanel = tabPanel;
26997     /**
26998      * The id for this TabPanelItem
26999      * @type String
27000      */
27001     this.id = id;
27002     /** @private */
27003     this.disabled = false;
27004     /** @private */
27005     this.text = text;
27006     /** @private */
27007     this.loaded = false;
27008     this.closable = closable;
27009
27010     /**
27011      * The body element for this TabPanelItem.
27012      * @type Roo.Element
27013      */
27014     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27015     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27016     this.bodyEl.setStyle("display", "block");
27017     this.bodyEl.setStyle("zoom", "1");
27018     this.hideAction();
27019
27020     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27021     /** @private */
27022     this.el = Roo.get(els.el, true);
27023     this.inner = Roo.get(els.inner, true);
27024     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27025     this.pnode = Roo.get(els.el.parentNode, true);
27026     this.el.on("mousedown", this.onTabMouseDown, this);
27027     this.el.on("click", this.onTabClick, this);
27028     /** @private */
27029     if(closable){
27030         var c = Roo.get(els.close, true);
27031         c.dom.title = this.closeText;
27032         c.addClassOnOver("close-over");
27033         c.on("click", this.closeClick, this);
27034      }
27035
27036     this.addEvents({
27037          /**
27038          * @event activate
27039          * Fires when this tab becomes the active tab.
27040          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27041          * @param {Roo.TabPanelItem} this
27042          */
27043         "activate": true,
27044         /**
27045          * @event beforeclose
27046          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27047          * @param {Roo.TabPanelItem} this
27048          * @param {Object} e Set cancel to true on this object to cancel the close.
27049          */
27050         "beforeclose": true,
27051         /**
27052          * @event close
27053          * Fires when this tab is closed.
27054          * @param {Roo.TabPanelItem} this
27055          */
27056          "close": true,
27057         /**
27058          * @event deactivate
27059          * Fires when this tab is no longer the active tab.
27060          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27061          * @param {Roo.TabPanelItem} this
27062          */
27063          "deactivate" : true
27064     });
27065     this.hidden = false;
27066
27067     Roo.TabPanelItem.superclass.constructor.call(this);
27068 };
27069
27070 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27071     purgeListeners : function(){
27072        Roo.util.Observable.prototype.purgeListeners.call(this);
27073        this.el.removeAllListeners();
27074     },
27075     /**
27076      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27077      */
27078     show : function(){
27079         this.pnode.addClass("on");
27080         this.showAction();
27081         if(Roo.isOpera){
27082             this.tabPanel.stripWrap.repaint();
27083         }
27084         this.fireEvent("activate", this.tabPanel, this);
27085     },
27086
27087     /**
27088      * Returns true if this tab is the active tab.
27089      * @return {Boolean}
27090      */
27091     isActive : function(){
27092         return this.tabPanel.getActiveTab() == this;
27093     },
27094
27095     /**
27096      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27097      */
27098     hide : function(){
27099         this.pnode.removeClass("on");
27100         this.hideAction();
27101         this.fireEvent("deactivate", this.tabPanel, this);
27102     },
27103
27104     hideAction : function(){
27105         this.bodyEl.hide();
27106         this.bodyEl.setStyle("position", "absolute");
27107         this.bodyEl.setLeft("-20000px");
27108         this.bodyEl.setTop("-20000px");
27109     },
27110
27111     showAction : function(){
27112         this.bodyEl.setStyle("position", "relative");
27113         this.bodyEl.setTop("");
27114         this.bodyEl.setLeft("");
27115         this.bodyEl.show();
27116     },
27117
27118     /**
27119      * Set the tooltip for the tab.
27120      * @param {String} tooltip The tab's tooltip
27121      */
27122     setTooltip : function(text){
27123         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27124             this.textEl.dom.qtip = text;
27125             this.textEl.dom.removeAttribute('title');
27126         }else{
27127             this.textEl.dom.title = text;
27128         }
27129     },
27130
27131     onTabClick : function(e){
27132         e.preventDefault();
27133         this.tabPanel.activate(this.id);
27134     },
27135
27136     onTabMouseDown : function(e){
27137         e.preventDefault();
27138         this.tabPanel.activate(this.id);
27139     },
27140
27141     getWidth : function(){
27142         return this.inner.getWidth();
27143     },
27144
27145     setWidth : function(width){
27146         var iwidth = width - this.pnode.getPadding("lr");
27147         this.inner.setWidth(iwidth);
27148         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27149         this.pnode.setWidth(width);
27150     },
27151
27152     /**
27153      * Show or hide the tab
27154      * @param {Boolean} hidden True to hide or false to show.
27155      */
27156     setHidden : function(hidden){
27157         this.hidden = hidden;
27158         this.pnode.setStyle("display", hidden ? "none" : "");
27159     },
27160
27161     /**
27162      * Returns true if this tab is "hidden"
27163      * @return {Boolean}
27164      */
27165     isHidden : function(){
27166         return this.hidden;
27167     },
27168
27169     /**
27170      * Returns the text for this tab
27171      * @return {String}
27172      */
27173     getText : function(){
27174         return this.text;
27175     },
27176
27177     autoSize : function(){
27178         //this.el.beginMeasure();
27179         this.textEl.setWidth(1);
27180         /*
27181          *  #2804 [new] Tabs in Roojs
27182          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27183          */
27184         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27185         //this.el.endMeasure();
27186     },
27187
27188     /**
27189      * Sets the text for the tab (Note: this also sets the tooltip text)
27190      * @param {String} text The tab's text and tooltip
27191      */
27192     setText : function(text){
27193         this.text = text;
27194         this.textEl.update(text);
27195         this.setTooltip(text);
27196         if(!this.tabPanel.resizeTabs){
27197             this.autoSize();
27198         }
27199     },
27200     /**
27201      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27202      */
27203     activate : function(){
27204         this.tabPanel.activate(this.id);
27205     },
27206
27207     /**
27208      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27209      */
27210     disable : function(){
27211         if(this.tabPanel.active != this){
27212             this.disabled = true;
27213             this.pnode.addClass("disabled");
27214         }
27215     },
27216
27217     /**
27218      * Enables this TabPanelItem if it was previously disabled.
27219      */
27220     enable : function(){
27221         this.disabled = false;
27222         this.pnode.removeClass("disabled");
27223     },
27224
27225     /**
27226      * Sets the content for this TabPanelItem.
27227      * @param {String} content The content
27228      * @param {Boolean} loadScripts true to look for and load scripts
27229      */
27230     setContent : function(content, loadScripts){
27231         this.bodyEl.update(content, loadScripts);
27232     },
27233
27234     /**
27235      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27236      * @return {Roo.UpdateManager} The UpdateManager
27237      */
27238     getUpdateManager : function(){
27239         return this.bodyEl.getUpdateManager();
27240     },
27241
27242     /**
27243      * Set a URL to be used to load the content for this TabPanelItem.
27244      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27245      * @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)
27246      * @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)
27247      * @return {Roo.UpdateManager} The UpdateManager
27248      */
27249     setUrl : function(url, params, loadOnce){
27250         if(this.refreshDelegate){
27251             this.un('activate', this.refreshDelegate);
27252         }
27253         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27254         this.on("activate", this.refreshDelegate);
27255         return this.bodyEl.getUpdateManager();
27256     },
27257
27258     /** @private */
27259     _handleRefresh : function(url, params, loadOnce){
27260         if(!loadOnce || !this.loaded){
27261             var updater = this.bodyEl.getUpdateManager();
27262             updater.update(url, params, this._setLoaded.createDelegate(this));
27263         }
27264     },
27265
27266     /**
27267      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27268      *   Will fail silently if the setUrl method has not been called.
27269      *   This does not activate the panel, just updates its content.
27270      */
27271     refresh : function(){
27272         if(this.refreshDelegate){
27273            this.loaded = false;
27274            this.refreshDelegate();
27275         }
27276     },
27277
27278     /** @private */
27279     _setLoaded : function(){
27280         this.loaded = true;
27281     },
27282
27283     /** @private */
27284     closeClick : function(e){
27285         var o = {};
27286         e.stopEvent();
27287         this.fireEvent("beforeclose", this, o);
27288         if(o.cancel !== true){
27289             this.tabPanel.removeTab(this.id);
27290         }
27291     },
27292     /**
27293      * The text displayed in the tooltip for the close icon.
27294      * @type String
27295      */
27296     closeText : "Close this tab"
27297 });
27298
27299 /** @private */
27300 Roo.TabPanel.prototype.createStrip = function(container){
27301     var strip = document.createElement("div");
27302     strip.className = "x-tabs-wrap";
27303     container.appendChild(strip);
27304     return strip;
27305 };
27306 /** @private */
27307 Roo.TabPanel.prototype.createStripList = function(strip){
27308     // div wrapper for retard IE
27309     // returns the "tr" element.
27310     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27311         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27312         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27313     return strip.firstChild.firstChild.firstChild.firstChild;
27314 };
27315 /** @private */
27316 Roo.TabPanel.prototype.createBody = function(container){
27317     var body = document.createElement("div");
27318     Roo.id(body, "tab-body");
27319     Roo.fly(body).addClass("x-tabs-body");
27320     container.appendChild(body);
27321     return body;
27322 };
27323 /** @private */
27324 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27325     var body = Roo.getDom(id);
27326     if(!body){
27327         body = document.createElement("div");
27328         body.id = id;
27329     }
27330     Roo.fly(body).addClass("x-tabs-item-body");
27331     bodyEl.insertBefore(body, bodyEl.firstChild);
27332     return body;
27333 };
27334 /** @private */
27335 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27336     var td = document.createElement("td");
27337     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27338     //stripEl.appendChild(td);
27339     if(closable){
27340         td.className = "x-tabs-closable";
27341         if(!this.closeTpl){
27342             this.closeTpl = new Roo.Template(
27343                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27344                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27345                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27346             );
27347         }
27348         var el = this.closeTpl.overwrite(td, {"text": text});
27349         var close = el.getElementsByTagName("div")[0];
27350         var inner = el.getElementsByTagName("em")[0];
27351         return {"el": el, "close": close, "inner": inner};
27352     } else {
27353         if(!this.tabTpl){
27354             this.tabTpl = new Roo.Template(
27355                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27356                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27357             );
27358         }
27359         var el = this.tabTpl.overwrite(td, {"text": text});
27360         var inner = el.getElementsByTagName("em")[0];
27361         return {"el": el, "inner": inner};
27362     }
27363 };/*
27364  * Based on:
27365  * Ext JS Library 1.1.1
27366  * Copyright(c) 2006-2007, Ext JS, LLC.
27367  *
27368  * Originally Released Under LGPL - original licence link has changed is not relivant.
27369  *
27370  * Fork - LGPL
27371  * <script type="text/javascript">
27372  */
27373
27374 /**
27375  * @class Roo.Button
27376  * @extends Roo.util.Observable
27377  * Simple Button class
27378  * @cfg {String} text The button text
27379  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27380  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27381  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27382  * @cfg {Object} scope The scope of the handler
27383  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27384  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27385  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27386  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27387  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27388  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27389    applies if enableToggle = true)
27390  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27391  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27392   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27393  * @constructor
27394  * Create a new button
27395  * @param {Object} config The config object
27396  */
27397 Roo.Button = function(renderTo, config)
27398 {
27399     if (!config) {
27400         config = renderTo;
27401         renderTo = config.renderTo || false;
27402     }
27403     
27404     Roo.apply(this, config);
27405     this.addEvents({
27406         /**
27407              * @event click
27408              * Fires when this button is clicked
27409              * @param {Button} this
27410              * @param {EventObject} e The click event
27411              */
27412             "click" : true,
27413         /**
27414              * @event toggle
27415              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27416              * @param {Button} this
27417              * @param {Boolean} pressed
27418              */
27419             "toggle" : true,
27420         /**
27421              * @event mouseover
27422              * Fires when the mouse hovers over the button
27423              * @param {Button} this
27424              * @param {Event} e The event object
27425              */
27426         'mouseover' : true,
27427         /**
27428              * @event mouseout
27429              * Fires when the mouse exits the button
27430              * @param {Button} this
27431              * @param {Event} e The event object
27432              */
27433         'mouseout': true,
27434          /**
27435              * @event render
27436              * Fires when the button is rendered
27437              * @param {Button} this
27438              */
27439         'render': true
27440     });
27441     if(this.menu){
27442         this.menu = Roo.menu.MenuMgr.get(this.menu);
27443     }
27444     // register listeners first!!  - so render can be captured..
27445     Roo.util.Observable.call(this);
27446     if(renderTo){
27447         this.render(renderTo);
27448     }
27449     
27450   
27451 };
27452
27453 Roo.extend(Roo.Button, Roo.util.Observable, {
27454     /**
27455      * 
27456      */
27457     
27458     /**
27459      * Read-only. True if this button is hidden
27460      * @type Boolean
27461      */
27462     hidden : false,
27463     /**
27464      * Read-only. True if this button is disabled
27465      * @type Boolean
27466      */
27467     disabled : false,
27468     /**
27469      * Read-only. True if this button is pressed (only if enableToggle = true)
27470      * @type Boolean
27471      */
27472     pressed : false,
27473
27474     /**
27475      * @cfg {Number} tabIndex 
27476      * The DOM tabIndex for this button (defaults to undefined)
27477      */
27478     tabIndex : undefined,
27479
27480     /**
27481      * @cfg {Boolean} enableToggle
27482      * True to enable pressed/not pressed toggling (defaults to false)
27483      */
27484     enableToggle: false,
27485     /**
27486      * @cfg {Mixed} menu
27487      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27488      */
27489     menu : undefined,
27490     /**
27491      * @cfg {String} menuAlign
27492      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27493      */
27494     menuAlign : "tl-bl?",
27495
27496     /**
27497      * @cfg {String} iconCls
27498      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27499      */
27500     iconCls : undefined,
27501     /**
27502      * @cfg {String} type
27503      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27504      */
27505     type : 'button',
27506
27507     // private
27508     menuClassTarget: 'tr',
27509
27510     /**
27511      * @cfg {String} clickEvent
27512      * The type of event to map to the button's event handler (defaults to 'click')
27513      */
27514     clickEvent : 'click',
27515
27516     /**
27517      * @cfg {Boolean} handleMouseEvents
27518      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27519      */
27520     handleMouseEvents : true,
27521
27522     /**
27523      * @cfg {String} tooltipType
27524      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27525      */
27526     tooltipType : 'qtip',
27527
27528     /**
27529      * @cfg {String} cls
27530      * A CSS class to apply to the button's main element.
27531      */
27532     
27533     /**
27534      * @cfg {Roo.Template} template (Optional)
27535      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27536      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27537      * require code modifications if required elements (e.g. a button) aren't present.
27538      */
27539
27540     // private
27541     render : function(renderTo){
27542         var btn;
27543         if(this.hideParent){
27544             this.parentEl = Roo.get(renderTo);
27545         }
27546         if(!this.dhconfig){
27547             if(!this.template){
27548                 if(!Roo.Button.buttonTemplate){
27549                     // hideous table template
27550                     Roo.Button.buttonTemplate = new Roo.Template(
27551                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27552                         '<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>',
27553                         "</tr></tbody></table>");
27554                 }
27555                 this.template = Roo.Button.buttonTemplate;
27556             }
27557             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27558             var btnEl = btn.child("button:first");
27559             btnEl.on('focus', this.onFocus, this);
27560             btnEl.on('blur', this.onBlur, this);
27561             if(this.cls){
27562                 btn.addClass(this.cls);
27563             }
27564             if(this.icon){
27565                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27566             }
27567             if(this.iconCls){
27568                 btnEl.addClass(this.iconCls);
27569                 if(!this.cls){
27570                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27571                 }
27572             }
27573             if(this.tabIndex !== undefined){
27574                 btnEl.dom.tabIndex = this.tabIndex;
27575             }
27576             if(this.tooltip){
27577                 if(typeof this.tooltip == 'object'){
27578                     Roo.QuickTips.tips(Roo.apply({
27579                           target: btnEl.id
27580                     }, this.tooltip));
27581                 } else {
27582                     btnEl.dom[this.tooltipType] = this.tooltip;
27583                 }
27584             }
27585         }else{
27586             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27587         }
27588         this.el = btn;
27589         if(this.id){
27590             this.el.dom.id = this.el.id = this.id;
27591         }
27592         if(this.menu){
27593             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27594             this.menu.on("show", this.onMenuShow, this);
27595             this.menu.on("hide", this.onMenuHide, this);
27596         }
27597         btn.addClass("x-btn");
27598         if(Roo.isIE && !Roo.isIE7){
27599             this.autoWidth.defer(1, this);
27600         }else{
27601             this.autoWidth();
27602         }
27603         if(this.handleMouseEvents){
27604             btn.on("mouseover", this.onMouseOver, this);
27605             btn.on("mouseout", this.onMouseOut, this);
27606             btn.on("mousedown", this.onMouseDown, this);
27607         }
27608         btn.on(this.clickEvent, this.onClick, this);
27609         //btn.on("mouseup", this.onMouseUp, this);
27610         if(this.hidden){
27611             this.hide();
27612         }
27613         if(this.disabled){
27614             this.disable();
27615         }
27616         Roo.ButtonToggleMgr.register(this);
27617         if(this.pressed){
27618             this.el.addClass("x-btn-pressed");
27619         }
27620         if(this.repeat){
27621             var repeater = new Roo.util.ClickRepeater(btn,
27622                 typeof this.repeat == "object" ? this.repeat : {}
27623             );
27624             repeater.on("click", this.onClick,  this);
27625         }
27626         
27627         this.fireEvent('render', this);
27628         
27629     },
27630     /**
27631      * Returns the button's underlying element
27632      * @return {Roo.Element} The element
27633      */
27634     getEl : function(){
27635         return this.el;  
27636     },
27637     
27638     /**
27639      * Destroys this Button and removes any listeners.
27640      */
27641     destroy : function(){
27642         Roo.ButtonToggleMgr.unregister(this);
27643         this.el.removeAllListeners();
27644         this.purgeListeners();
27645         this.el.remove();
27646     },
27647
27648     // private
27649     autoWidth : function(){
27650         if(this.el){
27651             this.el.setWidth("auto");
27652             if(Roo.isIE7 && Roo.isStrict){
27653                 var ib = this.el.child('button');
27654                 if(ib && ib.getWidth() > 20){
27655                     ib.clip();
27656                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27657                 }
27658             }
27659             if(this.minWidth){
27660                 if(this.hidden){
27661                     this.el.beginMeasure();
27662                 }
27663                 if(this.el.getWidth() < this.minWidth){
27664                     this.el.setWidth(this.minWidth);
27665                 }
27666                 if(this.hidden){
27667                     this.el.endMeasure();
27668                 }
27669             }
27670         }
27671     },
27672
27673     /**
27674      * Assigns this button's click handler
27675      * @param {Function} handler The function to call when the button is clicked
27676      * @param {Object} scope (optional) Scope for the function passed in
27677      */
27678     setHandler : function(handler, scope){
27679         this.handler = handler;
27680         this.scope = scope;  
27681     },
27682     
27683     /**
27684      * Sets this button's text
27685      * @param {String} text The button text
27686      */
27687     setText : function(text){
27688         this.text = text;
27689         if(this.el){
27690             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27691         }
27692         this.autoWidth();
27693     },
27694     
27695     /**
27696      * Gets the text for this button
27697      * @return {String} The button text
27698      */
27699     getText : function(){
27700         return this.text;  
27701     },
27702     
27703     /**
27704      * Show this button
27705      */
27706     show: function(){
27707         this.hidden = false;
27708         if(this.el){
27709             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27710         }
27711     },
27712     
27713     /**
27714      * Hide this button
27715      */
27716     hide: function(){
27717         this.hidden = true;
27718         if(this.el){
27719             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27720         }
27721     },
27722     
27723     /**
27724      * Convenience function for boolean show/hide
27725      * @param {Boolean} visible True to show, false to hide
27726      */
27727     setVisible: function(visible){
27728         if(visible) {
27729             this.show();
27730         }else{
27731             this.hide();
27732         }
27733     },
27734     
27735     /**
27736      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27737      * @param {Boolean} state (optional) Force a particular state
27738      */
27739     toggle : function(state){
27740         state = state === undefined ? !this.pressed : state;
27741         if(state != this.pressed){
27742             if(state){
27743                 this.el.addClass("x-btn-pressed");
27744                 this.pressed = true;
27745                 this.fireEvent("toggle", this, true);
27746             }else{
27747                 this.el.removeClass("x-btn-pressed");
27748                 this.pressed = false;
27749                 this.fireEvent("toggle", this, false);
27750             }
27751             if(this.toggleHandler){
27752                 this.toggleHandler.call(this.scope || this, this, state);
27753             }
27754         }
27755     },
27756     
27757     /**
27758      * Focus the button
27759      */
27760     focus : function(){
27761         this.el.child('button:first').focus();
27762     },
27763     
27764     /**
27765      * Disable this button
27766      */
27767     disable : function(){
27768         if(this.el){
27769             this.el.addClass("x-btn-disabled");
27770         }
27771         this.disabled = true;
27772     },
27773     
27774     /**
27775      * Enable this button
27776      */
27777     enable : function(){
27778         if(this.el){
27779             this.el.removeClass("x-btn-disabled");
27780         }
27781         this.disabled = false;
27782     },
27783
27784     /**
27785      * Convenience function for boolean enable/disable
27786      * @param {Boolean} enabled True to enable, false to disable
27787      */
27788     setDisabled : function(v){
27789         this[v !== true ? "enable" : "disable"]();
27790     },
27791
27792     // private
27793     onClick : function(e){
27794         if(e){
27795             e.preventDefault();
27796         }
27797         if(e.button != 0){
27798             return;
27799         }
27800         if(!this.disabled){
27801             if(this.enableToggle){
27802                 this.toggle();
27803             }
27804             if(this.menu && !this.menu.isVisible()){
27805                 this.menu.show(this.el, this.menuAlign);
27806             }
27807             this.fireEvent("click", this, e);
27808             if(this.handler){
27809                 this.el.removeClass("x-btn-over");
27810                 this.handler.call(this.scope || this, this, e);
27811             }
27812         }
27813     },
27814     // private
27815     onMouseOver : function(e){
27816         if(!this.disabled){
27817             this.el.addClass("x-btn-over");
27818             this.fireEvent('mouseover', this, e);
27819         }
27820     },
27821     // private
27822     onMouseOut : function(e){
27823         if(!e.within(this.el,  true)){
27824             this.el.removeClass("x-btn-over");
27825             this.fireEvent('mouseout', this, e);
27826         }
27827     },
27828     // private
27829     onFocus : function(e){
27830         if(!this.disabled){
27831             this.el.addClass("x-btn-focus");
27832         }
27833     },
27834     // private
27835     onBlur : function(e){
27836         this.el.removeClass("x-btn-focus");
27837     },
27838     // private
27839     onMouseDown : function(e){
27840         if(!this.disabled && e.button == 0){
27841             this.el.addClass("x-btn-click");
27842             Roo.get(document).on('mouseup', this.onMouseUp, this);
27843         }
27844     },
27845     // private
27846     onMouseUp : function(e){
27847         if(e.button == 0){
27848             this.el.removeClass("x-btn-click");
27849             Roo.get(document).un('mouseup', this.onMouseUp, this);
27850         }
27851     },
27852     // private
27853     onMenuShow : function(e){
27854         this.el.addClass("x-btn-menu-active");
27855     },
27856     // private
27857     onMenuHide : function(e){
27858         this.el.removeClass("x-btn-menu-active");
27859     }   
27860 });
27861
27862 // Private utility class used by Button
27863 Roo.ButtonToggleMgr = function(){
27864    var groups = {};
27865    
27866    function toggleGroup(btn, state){
27867        if(state){
27868            var g = groups[btn.toggleGroup];
27869            for(var i = 0, l = g.length; i < l; i++){
27870                if(g[i] != btn){
27871                    g[i].toggle(false);
27872                }
27873            }
27874        }
27875    }
27876    
27877    return {
27878        register : function(btn){
27879            if(!btn.toggleGroup){
27880                return;
27881            }
27882            var g = groups[btn.toggleGroup];
27883            if(!g){
27884                g = groups[btn.toggleGroup] = [];
27885            }
27886            g.push(btn);
27887            btn.on("toggle", toggleGroup);
27888        },
27889        
27890        unregister : function(btn){
27891            if(!btn.toggleGroup){
27892                return;
27893            }
27894            var g = groups[btn.toggleGroup];
27895            if(g){
27896                g.remove(btn);
27897                btn.un("toggle", toggleGroup);
27898            }
27899        }
27900    };
27901 }();/*
27902  * Based on:
27903  * Ext JS Library 1.1.1
27904  * Copyright(c) 2006-2007, Ext JS, LLC.
27905  *
27906  * Originally Released Under LGPL - original licence link has changed is not relivant.
27907  *
27908  * Fork - LGPL
27909  * <script type="text/javascript">
27910  */
27911  
27912 /**
27913  * @class Roo.SplitButton
27914  * @extends Roo.Button
27915  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27916  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27917  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27918  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27919  * @cfg {String} arrowTooltip The title attribute of the arrow
27920  * @constructor
27921  * Create a new menu button
27922  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27923  * @param {Object} config The config object
27924  */
27925 Roo.SplitButton = function(renderTo, config){
27926     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27927     /**
27928      * @event arrowclick
27929      * Fires when this button's arrow is clicked
27930      * @param {SplitButton} this
27931      * @param {EventObject} e The click event
27932      */
27933     this.addEvents({"arrowclick":true});
27934 };
27935
27936 Roo.extend(Roo.SplitButton, Roo.Button, {
27937     render : function(renderTo){
27938         // this is one sweet looking template!
27939         var tpl = new Roo.Template(
27940             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27941             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27942             '<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>',
27943             "</tbody></table></td><td>",
27944             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27945             '<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>',
27946             "</tbody></table></td></tr></table>"
27947         );
27948         var btn = tpl.append(renderTo, [this.text, this.type], true);
27949         var btnEl = btn.child("button");
27950         if(this.cls){
27951             btn.addClass(this.cls);
27952         }
27953         if(this.icon){
27954             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27955         }
27956         if(this.iconCls){
27957             btnEl.addClass(this.iconCls);
27958             if(!this.cls){
27959                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27960             }
27961         }
27962         this.el = btn;
27963         if(this.handleMouseEvents){
27964             btn.on("mouseover", this.onMouseOver, this);
27965             btn.on("mouseout", this.onMouseOut, this);
27966             btn.on("mousedown", this.onMouseDown, this);
27967             btn.on("mouseup", this.onMouseUp, this);
27968         }
27969         btn.on(this.clickEvent, this.onClick, this);
27970         if(this.tooltip){
27971             if(typeof this.tooltip == 'object'){
27972                 Roo.QuickTips.tips(Roo.apply({
27973                       target: btnEl.id
27974                 }, this.tooltip));
27975             } else {
27976                 btnEl.dom[this.tooltipType] = this.tooltip;
27977             }
27978         }
27979         if(this.arrowTooltip){
27980             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27981         }
27982         if(this.hidden){
27983             this.hide();
27984         }
27985         if(this.disabled){
27986             this.disable();
27987         }
27988         if(this.pressed){
27989             this.el.addClass("x-btn-pressed");
27990         }
27991         if(Roo.isIE && !Roo.isIE7){
27992             this.autoWidth.defer(1, this);
27993         }else{
27994             this.autoWidth();
27995         }
27996         if(this.menu){
27997             this.menu.on("show", this.onMenuShow, this);
27998             this.menu.on("hide", this.onMenuHide, this);
27999         }
28000         this.fireEvent('render', this);
28001     },
28002
28003     // private
28004     autoWidth : function(){
28005         if(this.el){
28006             var tbl = this.el.child("table:first");
28007             var tbl2 = this.el.child("table:last");
28008             this.el.setWidth("auto");
28009             tbl.setWidth("auto");
28010             if(Roo.isIE7 && Roo.isStrict){
28011                 var ib = this.el.child('button:first');
28012                 if(ib && ib.getWidth() > 20){
28013                     ib.clip();
28014                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28015                 }
28016             }
28017             if(this.minWidth){
28018                 if(this.hidden){
28019                     this.el.beginMeasure();
28020                 }
28021                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28022                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28023                 }
28024                 if(this.hidden){
28025                     this.el.endMeasure();
28026                 }
28027             }
28028             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28029         } 
28030     },
28031     /**
28032      * Sets this button's click handler
28033      * @param {Function} handler The function to call when the button is clicked
28034      * @param {Object} scope (optional) Scope for the function passed above
28035      */
28036     setHandler : function(handler, scope){
28037         this.handler = handler;
28038         this.scope = scope;  
28039     },
28040     
28041     /**
28042      * Sets this button's arrow click handler
28043      * @param {Function} handler The function to call when the arrow is clicked
28044      * @param {Object} scope (optional) Scope for the function passed above
28045      */
28046     setArrowHandler : function(handler, scope){
28047         this.arrowHandler = handler;
28048         this.scope = scope;  
28049     },
28050     
28051     /**
28052      * Focus the button
28053      */
28054     focus : function(){
28055         if(this.el){
28056             this.el.child("button:first").focus();
28057         }
28058     },
28059
28060     // private
28061     onClick : function(e){
28062         e.preventDefault();
28063         if(!this.disabled){
28064             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28065                 if(this.menu && !this.menu.isVisible()){
28066                     this.menu.show(this.el, this.menuAlign);
28067                 }
28068                 this.fireEvent("arrowclick", this, e);
28069                 if(this.arrowHandler){
28070                     this.arrowHandler.call(this.scope || this, this, e);
28071                 }
28072             }else{
28073                 this.fireEvent("click", this, e);
28074                 if(this.handler){
28075                     this.handler.call(this.scope || this, this, e);
28076                 }
28077             }
28078         }
28079     },
28080     // private
28081     onMouseDown : function(e){
28082         if(!this.disabled){
28083             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28084         }
28085     },
28086     // private
28087     onMouseUp : function(e){
28088         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28089     }   
28090 });
28091
28092
28093 // backwards compat
28094 Roo.MenuButton = Roo.SplitButton;/*
28095  * Based on:
28096  * Ext JS Library 1.1.1
28097  * Copyright(c) 2006-2007, Ext JS, LLC.
28098  *
28099  * Originally Released Under LGPL - original licence link has changed is not relivant.
28100  *
28101  * Fork - LGPL
28102  * <script type="text/javascript">
28103  */
28104
28105 /**
28106  * @class Roo.Toolbar
28107  * Basic Toolbar class.
28108  * @constructor
28109  * Creates a new Toolbar
28110  * @param {Object} container The config object
28111  */ 
28112 Roo.Toolbar = function(container, buttons, config)
28113 {
28114     /// old consturctor format still supported..
28115     if(container instanceof Array){ // omit the container for later rendering
28116         buttons = container;
28117         config = buttons;
28118         container = null;
28119     }
28120     if (typeof(container) == 'object' && container.xtype) {
28121         config = container;
28122         container = config.container;
28123         buttons = config.buttons || []; // not really - use items!!
28124     }
28125     var xitems = [];
28126     if (config && config.items) {
28127         xitems = config.items;
28128         delete config.items;
28129     }
28130     Roo.apply(this, config);
28131     this.buttons = buttons;
28132     
28133     if(container){
28134         this.render(container);
28135     }
28136     this.xitems = xitems;
28137     Roo.each(xitems, function(b) {
28138         this.add(b);
28139     }, this);
28140     
28141 };
28142
28143 Roo.Toolbar.prototype = {
28144     /**
28145      * @cfg {Array} items
28146      * array of button configs or elements to add (will be converted to a MixedCollection)
28147      */
28148     
28149     /**
28150      * @cfg {String/HTMLElement/Element} container
28151      * The id or element that will contain the toolbar
28152      */
28153     // private
28154     render : function(ct){
28155         this.el = Roo.get(ct);
28156         if(this.cls){
28157             this.el.addClass(this.cls);
28158         }
28159         // using a table allows for vertical alignment
28160         // 100% width is needed by Safari...
28161         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28162         this.tr = this.el.child("tr", true);
28163         var autoId = 0;
28164         this.items = new Roo.util.MixedCollection(false, function(o){
28165             return o.id || ("item" + (++autoId));
28166         });
28167         if(this.buttons){
28168             this.add.apply(this, this.buttons);
28169             delete this.buttons;
28170         }
28171     },
28172
28173     /**
28174      * Adds element(s) to the toolbar -- this function takes a variable number of 
28175      * arguments of mixed type and adds them to the toolbar.
28176      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28177      * <ul>
28178      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28179      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28180      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28181      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28182      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28183      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28184      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28185      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28186      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28187      * </ul>
28188      * @param {Mixed} arg2
28189      * @param {Mixed} etc.
28190      */
28191     add : function(){
28192         var a = arguments, l = a.length;
28193         for(var i = 0; i < l; i++){
28194             this._add(a[i]);
28195         }
28196     },
28197     // private..
28198     _add : function(el) {
28199         
28200         if (el.xtype) {
28201             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28202         }
28203         
28204         if (el.applyTo){ // some kind of form field
28205             return this.addField(el);
28206         } 
28207         if (el.render){ // some kind of Toolbar.Item
28208             return this.addItem(el);
28209         }
28210         if (typeof el == "string"){ // string
28211             if(el == "separator" || el == "-"){
28212                 return this.addSeparator();
28213             }
28214             if (el == " "){
28215                 return this.addSpacer();
28216             }
28217             if(el == "->"){
28218                 return this.addFill();
28219             }
28220             return this.addText(el);
28221             
28222         }
28223         if(el.tagName){ // element
28224             return this.addElement(el);
28225         }
28226         if(typeof el == "object"){ // must be button config?
28227             return this.addButton(el);
28228         }
28229         // and now what?!?!
28230         return false;
28231         
28232     },
28233     
28234     /**
28235      * Add an Xtype element
28236      * @param {Object} xtype Xtype Object
28237      * @return {Object} created Object
28238      */
28239     addxtype : function(e){
28240         return this.add(e);  
28241     },
28242     
28243     /**
28244      * Returns the Element for this toolbar.
28245      * @return {Roo.Element}
28246      */
28247     getEl : function(){
28248         return this.el;  
28249     },
28250     
28251     /**
28252      * Adds a separator
28253      * @return {Roo.Toolbar.Item} The separator item
28254      */
28255     addSeparator : function(){
28256         return this.addItem(new Roo.Toolbar.Separator());
28257     },
28258
28259     /**
28260      * Adds a spacer element
28261      * @return {Roo.Toolbar.Spacer} The spacer item
28262      */
28263     addSpacer : function(){
28264         return this.addItem(new Roo.Toolbar.Spacer());
28265     },
28266
28267     /**
28268      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28269      * @return {Roo.Toolbar.Fill} The fill item
28270      */
28271     addFill : function(){
28272         return this.addItem(new Roo.Toolbar.Fill());
28273     },
28274
28275     /**
28276      * Adds any standard HTML element to the toolbar
28277      * @param {String/HTMLElement/Element} el The element or id of the element to add
28278      * @return {Roo.Toolbar.Item} The element's item
28279      */
28280     addElement : function(el){
28281         return this.addItem(new Roo.Toolbar.Item(el));
28282     },
28283     /**
28284      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28285      * @type Roo.util.MixedCollection  
28286      */
28287     items : false,
28288      
28289     /**
28290      * Adds any Toolbar.Item or subclass
28291      * @param {Roo.Toolbar.Item} item
28292      * @return {Roo.Toolbar.Item} The item
28293      */
28294     addItem : function(item){
28295         var td = this.nextBlock();
28296         item.render(td);
28297         this.items.add(item);
28298         return item;
28299     },
28300     
28301     /**
28302      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28303      * @param {Object/Array} config A button config or array of configs
28304      * @return {Roo.Toolbar.Button/Array}
28305      */
28306     addButton : function(config){
28307         if(config instanceof Array){
28308             var buttons = [];
28309             for(var i = 0, len = config.length; i < len; i++) {
28310                 buttons.push(this.addButton(config[i]));
28311             }
28312             return buttons;
28313         }
28314         var b = config;
28315         if(!(config instanceof Roo.Toolbar.Button)){
28316             b = config.split ?
28317                 new Roo.Toolbar.SplitButton(config) :
28318                 new Roo.Toolbar.Button(config);
28319         }
28320         var td = this.nextBlock();
28321         b.render(td);
28322         this.items.add(b);
28323         return b;
28324     },
28325     
28326     /**
28327      * Adds text to the toolbar
28328      * @param {String} text The text to add
28329      * @return {Roo.Toolbar.Item} The element's item
28330      */
28331     addText : function(text){
28332         return this.addItem(new Roo.Toolbar.TextItem(text));
28333     },
28334     
28335     /**
28336      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28337      * @param {Number} index The index where the item is to be inserted
28338      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28339      * @return {Roo.Toolbar.Button/Item}
28340      */
28341     insertButton : function(index, item){
28342         if(item instanceof Array){
28343             var buttons = [];
28344             for(var i = 0, len = item.length; i < len; i++) {
28345                buttons.push(this.insertButton(index + i, item[i]));
28346             }
28347             return buttons;
28348         }
28349         if (!(item instanceof Roo.Toolbar.Button)){
28350            item = new Roo.Toolbar.Button(item);
28351         }
28352         var td = document.createElement("td");
28353         this.tr.insertBefore(td, this.tr.childNodes[index]);
28354         item.render(td);
28355         this.items.insert(index, item);
28356         return item;
28357     },
28358     
28359     /**
28360      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28361      * @param {Object} config
28362      * @return {Roo.Toolbar.Item} The element's item
28363      */
28364     addDom : function(config, returnEl){
28365         var td = this.nextBlock();
28366         Roo.DomHelper.overwrite(td, config);
28367         var ti = new Roo.Toolbar.Item(td.firstChild);
28368         ti.render(td);
28369         this.items.add(ti);
28370         return ti;
28371     },
28372
28373     /**
28374      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28375      * @type Roo.util.MixedCollection  
28376      */
28377     fields : false,
28378     
28379     /**
28380      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28381      * Note: the field should not have been rendered yet. For a field that has already been
28382      * rendered, use {@link #addElement}.
28383      * @param {Roo.form.Field} field
28384      * @return {Roo.ToolbarItem}
28385      */
28386      
28387       
28388     addField : function(field) {
28389         if (!this.fields) {
28390             var autoId = 0;
28391             this.fields = new Roo.util.MixedCollection(false, function(o){
28392                 return o.id || ("item" + (++autoId));
28393             });
28394
28395         }
28396         
28397         var td = this.nextBlock();
28398         field.render(td);
28399         var ti = new Roo.Toolbar.Item(td.firstChild);
28400         ti.render(td);
28401         this.items.add(ti);
28402         this.fields.add(field);
28403         return ti;
28404     },
28405     /**
28406      * Hide the toolbar
28407      * @method hide
28408      */
28409      
28410       
28411     hide : function()
28412     {
28413         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28414         this.el.child('div').hide();
28415     },
28416     /**
28417      * Show the toolbar
28418      * @method show
28419      */
28420     show : function()
28421     {
28422         this.el.child('div').show();
28423     },
28424       
28425     // private
28426     nextBlock : function(){
28427         var td = document.createElement("td");
28428         this.tr.appendChild(td);
28429         return td;
28430     },
28431
28432     // private
28433     destroy : function(){
28434         if(this.items){ // rendered?
28435             Roo.destroy.apply(Roo, this.items.items);
28436         }
28437         if(this.fields){ // rendered?
28438             Roo.destroy.apply(Roo, this.fields.items);
28439         }
28440         Roo.Element.uncache(this.el, this.tr);
28441     }
28442 };
28443
28444 /**
28445  * @class Roo.Toolbar.Item
28446  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28447  * @constructor
28448  * Creates a new Item
28449  * @param {HTMLElement} el 
28450  */
28451 Roo.Toolbar.Item = function(el){
28452     this.el = Roo.getDom(el);
28453     this.id = Roo.id(this.el);
28454     this.hidden = false;
28455 };
28456
28457 Roo.Toolbar.Item.prototype = {
28458     
28459     /**
28460      * Get this item's HTML Element
28461      * @return {HTMLElement}
28462      */
28463     getEl : function(){
28464        return this.el;  
28465     },
28466
28467     // private
28468     render : function(td){
28469         this.td = td;
28470         td.appendChild(this.el);
28471     },
28472     
28473     /**
28474      * Removes and destroys this item.
28475      */
28476     destroy : function(){
28477         this.td.parentNode.removeChild(this.td);
28478     },
28479     
28480     /**
28481      * Shows this item.
28482      */
28483     show: function(){
28484         this.hidden = false;
28485         this.td.style.display = "";
28486     },
28487     
28488     /**
28489      * Hides this item.
28490      */
28491     hide: function(){
28492         this.hidden = true;
28493         this.td.style.display = "none";
28494     },
28495     
28496     /**
28497      * Convenience function for boolean show/hide.
28498      * @param {Boolean} visible true to show/false to hide
28499      */
28500     setVisible: function(visible){
28501         if(visible) {
28502             this.show();
28503         }else{
28504             this.hide();
28505         }
28506     },
28507     
28508     /**
28509      * Try to focus this item.
28510      */
28511     focus : function(){
28512         Roo.fly(this.el).focus();
28513     },
28514     
28515     /**
28516      * Disables this item.
28517      */
28518     disable : function(){
28519         Roo.fly(this.td).addClass("x-item-disabled");
28520         this.disabled = true;
28521         this.el.disabled = true;
28522     },
28523     
28524     /**
28525      * Enables this item.
28526      */
28527     enable : function(){
28528         Roo.fly(this.td).removeClass("x-item-disabled");
28529         this.disabled = false;
28530         this.el.disabled = false;
28531     }
28532 };
28533
28534
28535 /**
28536  * @class Roo.Toolbar.Separator
28537  * @extends Roo.Toolbar.Item
28538  * A simple toolbar separator class
28539  * @constructor
28540  * Creates a new Separator
28541  */
28542 Roo.Toolbar.Separator = function(){
28543     var s = document.createElement("span");
28544     s.className = "ytb-sep";
28545     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28546 };
28547 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28548     enable:Roo.emptyFn,
28549     disable:Roo.emptyFn,
28550     focus:Roo.emptyFn
28551 });
28552
28553 /**
28554  * @class Roo.Toolbar.Spacer
28555  * @extends Roo.Toolbar.Item
28556  * A simple element that adds extra horizontal space to a toolbar.
28557  * @constructor
28558  * Creates a new Spacer
28559  */
28560 Roo.Toolbar.Spacer = function(){
28561     var s = document.createElement("div");
28562     s.className = "ytb-spacer";
28563     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28564 };
28565 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28566     enable:Roo.emptyFn,
28567     disable:Roo.emptyFn,
28568     focus:Roo.emptyFn
28569 });
28570
28571 /**
28572  * @class Roo.Toolbar.Fill
28573  * @extends Roo.Toolbar.Spacer
28574  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28575  * @constructor
28576  * Creates a new Spacer
28577  */
28578 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28579     // private
28580     render : function(td){
28581         td.style.width = '100%';
28582         Roo.Toolbar.Fill.superclass.render.call(this, td);
28583     }
28584 });
28585
28586 /**
28587  * @class Roo.Toolbar.TextItem
28588  * @extends Roo.Toolbar.Item
28589  * A simple class that renders text directly into a toolbar.
28590  * @constructor
28591  * Creates a new TextItem
28592  * @param {String} text
28593  */
28594 Roo.Toolbar.TextItem = function(text){
28595     if (typeof(text) == 'object') {
28596         text = text.text;
28597     }
28598     var s = document.createElement("span");
28599     s.className = "ytb-text";
28600     s.innerHTML = text;
28601     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28602 };
28603 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28604     enable:Roo.emptyFn,
28605     disable:Roo.emptyFn,
28606     focus:Roo.emptyFn
28607 });
28608
28609 /**
28610  * @class Roo.Toolbar.Button
28611  * @extends Roo.Button
28612  * A button that renders into a toolbar.
28613  * @constructor
28614  * Creates a new Button
28615  * @param {Object} config A standard {@link Roo.Button} config object
28616  */
28617 Roo.Toolbar.Button = function(config){
28618     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28619 };
28620 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28621     render : function(td){
28622         this.td = td;
28623         Roo.Toolbar.Button.superclass.render.call(this, td);
28624     },
28625     
28626     /**
28627      * Removes and destroys this button
28628      */
28629     destroy : function(){
28630         Roo.Toolbar.Button.superclass.destroy.call(this);
28631         this.td.parentNode.removeChild(this.td);
28632     },
28633     
28634     /**
28635      * Shows this button
28636      */
28637     show: function(){
28638         this.hidden = false;
28639         this.td.style.display = "";
28640     },
28641     
28642     /**
28643      * Hides this button
28644      */
28645     hide: function(){
28646         this.hidden = true;
28647         this.td.style.display = "none";
28648     },
28649
28650     /**
28651      * Disables this item
28652      */
28653     disable : function(){
28654         Roo.fly(this.td).addClass("x-item-disabled");
28655         this.disabled = true;
28656     },
28657
28658     /**
28659      * Enables this item
28660      */
28661     enable : function(){
28662         Roo.fly(this.td).removeClass("x-item-disabled");
28663         this.disabled = false;
28664     }
28665 });
28666 // backwards compat
28667 Roo.ToolbarButton = Roo.Toolbar.Button;
28668
28669 /**
28670  * @class Roo.Toolbar.SplitButton
28671  * @extends Roo.SplitButton
28672  * A menu button that renders into a toolbar.
28673  * @constructor
28674  * Creates a new SplitButton
28675  * @param {Object} config A standard {@link Roo.SplitButton} config object
28676  */
28677 Roo.Toolbar.SplitButton = function(config){
28678     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28679 };
28680 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28681     render : function(td){
28682         this.td = td;
28683         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28684     },
28685     
28686     /**
28687      * Removes and destroys this button
28688      */
28689     destroy : function(){
28690         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28691         this.td.parentNode.removeChild(this.td);
28692     },
28693     
28694     /**
28695      * Shows this button
28696      */
28697     show: function(){
28698         this.hidden = false;
28699         this.td.style.display = "";
28700     },
28701     
28702     /**
28703      * Hides this button
28704      */
28705     hide: function(){
28706         this.hidden = true;
28707         this.td.style.display = "none";
28708     }
28709 });
28710
28711 // backwards compat
28712 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28713  * Based on:
28714  * Ext JS Library 1.1.1
28715  * Copyright(c) 2006-2007, Ext JS, LLC.
28716  *
28717  * Originally Released Under LGPL - original licence link has changed is not relivant.
28718  *
28719  * Fork - LGPL
28720  * <script type="text/javascript">
28721  */
28722  
28723 /**
28724  * @class Roo.PagingToolbar
28725  * @extends Roo.Toolbar
28726  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28727  * @constructor
28728  * Create a new PagingToolbar
28729  * @param {Object} config The config object
28730  */
28731 Roo.PagingToolbar = function(el, ds, config)
28732 {
28733     // old args format still supported... - xtype is prefered..
28734     if (typeof(el) == 'object' && el.xtype) {
28735         // created from xtype...
28736         config = el;
28737         ds = el.dataSource;
28738         el = config.container;
28739     }
28740     var items = [];
28741     if (config.items) {
28742         items = config.items;
28743         config.items = [];
28744     }
28745     
28746     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28747     this.ds = ds;
28748     this.cursor = 0;
28749     this.renderButtons(this.el);
28750     this.bind(ds);
28751     
28752     // supprot items array.
28753    
28754     Roo.each(items, function(e) {
28755         this.add(Roo.factory(e));
28756     },this);
28757     
28758 };
28759
28760 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28761     /**
28762      * @cfg {Roo.data.Store} dataSource
28763      * The underlying data store providing the paged data
28764      */
28765     /**
28766      * @cfg {String/HTMLElement/Element} container
28767      * container The id or element that will contain the toolbar
28768      */
28769     /**
28770      * @cfg {Boolean} displayInfo
28771      * True to display the displayMsg (defaults to false)
28772      */
28773     /**
28774      * @cfg {Number} pageSize
28775      * The number of records to display per page (defaults to 20)
28776      */
28777     pageSize: 20,
28778     /**
28779      * @cfg {String} displayMsg
28780      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28781      */
28782     displayMsg : 'Displaying {0} - {1} of {2}',
28783     /**
28784      * @cfg {String} emptyMsg
28785      * The message to display when no records are found (defaults to "No data to display")
28786      */
28787     emptyMsg : 'No data to display',
28788     /**
28789      * Customizable piece of the default paging text (defaults to "Page")
28790      * @type String
28791      */
28792     beforePageText : "Page",
28793     /**
28794      * Customizable piece of the default paging text (defaults to "of %0")
28795      * @type String
28796      */
28797     afterPageText : "of {0}",
28798     /**
28799      * Customizable piece of the default paging text (defaults to "First Page")
28800      * @type String
28801      */
28802     firstText : "First Page",
28803     /**
28804      * Customizable piece of the default paging text (defaults to "Previous Page")
28805      * @type String
28806      */
28807     prevText : "Previous Page",
28808     /**
28809      * Customizable piece of the default paging text (defaults to "Next Page")
28810      * @type String
28811      */
28812     nextText : "Next Page",
28813     /**
28814      * Customizable piece of the default paging text (defaults to "Last Page")
28815      * @type String
28816      */
28817     lastText : "Last Page",
28818     /**
28819      * Customizable piece of the default paging text (defaults to "Refresh")
28820      * @type String
28821      */
28822     refreshText : "Refresh",
28823
28824     // private
28825     renderButtons : function(el){
28826         Roo.PagingToolbar.superclass.render.call(this, el);
28827         this.first = this.addButton({
28828             tooltip: this.firstText,
28829             cls: "x-btn-icon x-grid-page-first",
28830             disabled: true,
28831             handler: this.onClick.createDelegate(this, ["first"])
28832         });
28833         this.prev = this.addButton({
28834             tooltip: this.prevText,
28835             cls: "x-btn-icon x-grid-page-prev",
28836             disabled: true,
28837             handler: this.onClick.createDelegate(this, ["prev"])
28838         });
28839         //this.addSeparator();
28840         this.add(this.beforePageText);
28841         this.field = Roo.get(this.addDom({
28842            tag: "input",
28843            type: "text",
28844            size: "3",
28845            value: "1",
28846            cls: "x-grid-page-number"
28847         }).el);
28848         this.field.on("keydown", this.onPagingKeydown, this);
28849         this.field.on("focus", function(){this.dom.select();});
28850         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28851         this.field.setHeight(18);
28852         //this.addSeparator();
28853         this.next = this.addButton({
28854             tooltip: this.nextText,
28855             cls: "x-btn-icon x-grid-page-next",
28856             disabled: true,
28857             handler: this.onClick.createDelegate(this, ["next"])
28858         });
28859         this.last = this.addButton({
28860             tooltip: this.lastText,
28861             cls: "x-btn-icon x-grid-page-last",
28862             disabled: true,
28863             handler: this.onClick.createDelegate(this, ["last"])
28864         });
28865         //this.addSeparator();
28866         this.loading = this.addButton({
28867             tooltip: this.refreshText,
28868             cls: "x-btn-icon x-grid-loading",
28869             handler: this.onClick.createDelegate(this, ["refresh"])
28870         });
28871
28872         if(this.displayInfo){
28873             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28874         }
28875     },
28876
28877     // private
28878     updateInfo : function(){
28879         if(this.displayEl){
28880             var count = this.ds.getCount();
28881             var msg = count == 0 ?
28882                 this.emptyMsg :
28883                 String.format(
28884                     this.displayMsg,
28885                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28886                 );
28887             this.displayEl.update(msg);
28888         }
28889     },
28890
28891     // private
28892     onLoad : function(ds, r, o){
28893        this.cursor = o.params ? o.params.start : 0;
28894        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28895
28896        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28897        this.field.dom.value = ap;
28898        this.first.setDisabled(ap == 1);
28899        this.prev.setDisabled(ap == 1);
28900        this.next.setDisabled(ap == ps);
28901        this.last.setDisabled(ap == ps);
28902        this.loading.enable();
28903        this.updateInfo();
28904     },
28905
28906     // private
28907     getPageData : function(){
28908         var total = this.ds.getTotalCount();
28909         return {
28910             total : total,
28911             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28912             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28913         };
28914     },
28915
28916     // private
28917     onLoadError : function(){
28918         this.loading.enable();
28919     },
28920
28921     // private
28922     onPagingKeydown : function(e){
28923         var k = e.getKey();
28924         var d = this.getPageData();
28925         if(k == e.RETURN){
28926             var v = this.field.dom.value, pageNum;
28927             if(!v || isNaN(pageNum = parseInt(v, 10))){
28928                 this.field.dom.value = d.activePage;
28929                 return;
28930             }
28931             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28932             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28933             e.stopEvent();
28934         }
28935         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))
28936         {
28937           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28938           this.field.dom.value = pageNum;
28939           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28940           e.stopEvent();
28941         }
28942         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28943         {
28944           var v = this.field.dom.value, pageNum; 
28945           var increment = (e.shiftKey) ? 10 : 1;
28946           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28947             increment *= -1;
28948           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28949             this.field.dom.value = d.activePage;
28950             return;
28951           }
28952           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28953           {
28954             this.field.dom.value = parseInt(v, 10) + increment;
28955             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28956             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28957           }
28958           e.stopEvent();
28959         }
28960     },
28961
28962     // private
28963     beforeLoad : function(){
28964         if(this.loading){
28965             this.loading.disable();
28966         }
28967     },
28968
28969     // private
28970     onClick : function(which){
28971         var ds = this.ds;
28972         switch(which){
28973             case "first":
28974                 ds.load({params:{start: 0, limit: this.pageSize}});
28975             break;
28976             case "prev":
28977                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28978             break;
28979             case "next":
28980                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28981             break;
28982             case "last":
28983                 var total = ds.getTotalCount();
28984                 var extra = total % this.pageSize;
28985                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28986                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28987             break;
28988             case "refresh":
28989                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28990             break;
28991         }
28992     },
28993
28994     /**
28995      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28996      * @param {Roo.data.Store} store The data store to unbind
28997      */
28998     unbind : function(ds){
28999         ds.un("beforeload", this.beforeLoad, this);
29000         ds.un("load", this.onLoad, this);
29001         ds.un("loadexception", this.onLoadError, this);
29002         ds.un("remove", this.updateInfo, this);
29003         ds.un("add", this.updateInfo, this);
29004         this.ds = undefined;
29005     },
29006
29007     /**
29008      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29009      * @param {Roo.data.Store} store The data store to bind
29010      */
29011     bind : function(ds){
29012         ds.on("beforeload", this.beforeLoad, this);
29013         ds.on("load", this.onLoad, this);
29014         ds.on("loadexception", this.onLoadError, this);
29015         ds.on("remove", this.updateInfo, this);
29016         ds.on("add", this.updateInfo, this);
29017         this.ds = ds;
29018     }
29019 });/*
29020  * Based on:
29021  * Ext JS Library 1.1.1
29022  * Copyright(c) 2006-2007, Ext JS, LLC.
29023  *
29024  * Originally Released Under LGPL - original licence link has changed is not relivant.
29025  *
29026  * Fork - LGPL
29027  * <script type="text/javascript">
29028  */
29029
29030 /**
29031  * @class Roo.Resizable
29032  * @extends Roo.util.Observable
29033  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29034  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29035  * 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
29036  * the element will be wrapped for you automatically.</p>
29037  * <p>Here is the list of valid resize handles:</p>
29038  * <pre>
29039 Value   Description
29040 ------  -------------------
29041  'n'     north
29042  's'     south
29043  'e'     east
29044  'w'     west
29045  'nw'    northwest
29046  'sw'    southwest
29047  'se'    southeast
29048  'ne'    northeast
29049  'hd'    horizontal drag
29050  'all'   all
29051 </pre>
29052  * <p>Here's an example showing the creation of a typical Resizable:</p>
29053  * <pre><code>
29054 var resizer = new Roo.Resizable("element-id", {
29055     handles: 'all',
29056     minWidth: 200,
29057     minHeight: 100,
29058     maxWidth: 500,
29059     maxHeight: 400,
29060     pinned: true
29061 });
29062 resizer.on("resize", myHandler);
29063 </code></pre>
29064  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29065  * resizer.east.setDisplayed(false);</p>
29066  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29067  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29068  * resize operation's new size (defaults to [0, 0])
29069  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29070  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29071  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29072  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29073  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29074  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29075  * @cfg {Number} width The width of the element in pixels (defaults to null)
29076  * @cfg {Number} height The height of the element in pixels (defaults to null)
29077  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29078  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29079  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29080  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29081  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29082  * in favor of the handles config option (defaults to false)
29083  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29084  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29085  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29086  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29087  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29088  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29089  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29090  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29091  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29092  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29093  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29094  * @constructor
29095  * Create a new resizable component
29096  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29097  * @param {Object} config configuration options
29098   */
29099 Roo.Resizable = function(el, config)
29100 {
29101     this.el = Roo.get(el);
29102
29103     if(config && config.wrap){
29104         config.resizeChild = this.el;
29105         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29106         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29107         this.el.setStyle("overflow", "hidden");
29108         this.el.setPositioning(config.resizeChild.getPositioning());
29109         config.resizeChild.clearPositioning();
29110         if(!config.width || !config.height){
29111             var csize = config.resizeChild.getSize();
29112             this.el.setSize(csize.width, csize.height);
29113         }
29114         if(config.pinned && !config.adjustments){
29115             config.adjustments = "auto";
29116         }
29117     }
29118
29119     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29120     this.proxy.unselectable();
29121     this.proxy.enableDisplayMode('block');
29122
29123     Roo.apply(this, config);
29124
29125     if(this.pinned){
29126         this.disableTrackOver = true;
29127         this.el.addClass("x-resizable-pinned");
29128     }
29129     // if the element isn't positioned, make it relative
29130     var position = this.el.getStyle("position");
29131     if(position != "absolute" && position != "fixed"){
29132         this.el.setStyle("position", "relative");
29133     }
29134     if(!this.handles){ // no handles passed, must be legacy style
29135         this.handles = 's,e,se';
29136         if(this.multiDirectional){
29137             this.handles += ',n,w';
29138         }
29139     }
29140     if(this.handles == "all"){
29141         this.handles = "n s e w ne nw se sw";
29142     }
29143     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29144     var ps = Roo.Resizable.positions;
29145     for(var i = 0, len = hs.length; i < len; i++){
29146         if(hs[i] && ps[hs[i]]){
29147             var pos = ps[hs[i]];
29148             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29149         }
29150     }
29151     // legacy
29152     this.corner = this.southeast;
29153     
29154     // updateBox = the box can move..
29155     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29156         this.updateBox = true;
29157     }
29158
29159     this.activeHandle = null;
29160
29161     if(this.resizeChild){
29162         if(typeof this.resizeChild == "boolean"){
29163             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29164         }else{
29165             this.resizeChild = Roo.get(this.resizeChild, true);
29166         }
29167     }
29168     
29169     if(this.adjustments == "auto"){
29170         var rc = this.resizeChild;
29171         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29172         if(rc && (hw || hn)){
29173             rc.position("relative");
29174             rc.setLeft(hw ? hw.el.getWidth() : 0);
29175             rc.setTop(hn ? hn.el.getHeight() : 0);
29176         }
29177         this.adjustments = [
29178             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29179             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29180         ];
29181     }
29182
29183     if(this.draggable){
29184         this.dd = this.dynamic ?
29185             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29186         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29187     }
29188
29189     // public events
29190     this.addEvents({
29191         /**
29192          * @event beforeresize
29193          * Fired before resize is allowed. Set enabled to false to cancel resize.
29194          * @param {Roo.Resizable} this
29195          * @param {Roo.EventObject} e The mousedown event
29196          */
29197         "beforeresize" : true,
29198         /**
29199          * @event resizing
29200          * Fired a resizing.
29201          * @param {Roo.Resizable} this
29202          * @param {Number} x The new x position
29203          * @param {Number} y The new y position
29204          * @param {Number} w The new w width
29205          * @param {Number} h The new h hight
29206          * @param {Roo.EventObject} e The mouseup event
29207          */
29208         "resizing" : true,
29209         /**
29210          * @event resize
29211          * Fired after a resize.
29212          * @param {Roo.Resizable} this
29213          * @param {Number} width The new width
29214          * @param {Number} height The new height
29215          * @param {Roo.EventObject} e The mouseup event
29216          */
29217         "resize" : true
29218     });
29219
29220     if(this.width !== null && this.height !== null){
29221         this.resizeTo(this.width, this.height);
29222     }else{
29223         this.updateChildSize();
29224     }
29225     if(Roo.isIE){
29226         this.el.dom.style.zoom = 1;
29227     }
29228     Roo.Resizable.superclass.constructor.call(this);
29229 };
29230
29231 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29232         resizeChild : false,
29233         adjustments : [0, 0],
29234         minWidth : 5,
29235         minHeight : 5,
29236         maxWidth : 10000,
29237         maxHeight : 10000,
29238         enabled : true,
29239         animate : false,
29240         duration : .35,
29241         dynamic : false,
29242         handles : false,
29243         multiDirectional : false,
29244         disableTrackOver : false,
29245         easing : 'easeOutStrong',
29246         widthIncrement : 0,
29247         heightIncrement : 0,
29248         pinned : false,
29249         width : null,
29250         height : null,
29251         preserveRatio : false,
29252         transparent: false,
29253         minX: 0,
29254         minY: 0,
29255         draggable: false,
29256
29257         /**
29258          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29259          */
29260         constrainTo: undefined,
29261         /**
29262          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29263          */
29264         resizeRegion: undefined,
29265
29266
29267     /**
29268      * Perform a manual resize
29269      * @param {Number} width
29270      * @param {Number} height
29271      */
29272     resizeTo : function(width, height){
29273         this.el.setSize(width, height);
29274         this.updateChildSize();
29275         this.fireEvent("resize", this, width, height, null);
29276     },
29277
29278     // private
29279     startSizing : function(e, handle){
29280         this.fireEvent("beforeresize", this, e);
29281         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29282
29283             if(!this.overlay){
29284                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29285                 this.overlay.unselectable();
29286                 this.overlay.enableDisplayMode("block");
29287                 this.overlay.on("mousemove", this.onMouseMove, this);
29288                 this.overlay.on("mouseup", this.onMouseUp, this);
29289             }
29290             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29291
29292             this.resizing = true;
29293             this.startBox = this.el.getBox();
29294             this.startPoint = e.getXY();
29295             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29296                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29297
29298             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29299             this.overlay.show();
29300
29301             if(this.constrainTo) {
29302                 var ct = Roo.get(this.constrainTo);
29303                 this.resizeRegion = ct.getRegion().adjust(
29304                     ct.getFrameWidth('t'),
29305                     ct.getFrameWidth('l'),
29306                     -ct.getFrameWidth('b'),
29307                     -ct.getFrameWidth('r')
29308                 );
29309             }
29310
29311             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29312             this.proxy.show();
29313             this.proxy.setBox(this.startBox);
29314             if(!this.dynamic){
29315                 this.proxy.setStyle('visibility', 'visible');
29316             }
29317         }
29318     },
29319
29320     // private
29321     onMouseDown : function(handle, e){
29322         if(this.enabled){
29323             e.stopEvent();
29324             this.activeHandle = handle;
29325             this.startSizing(e, handle);
29326         }
29327     },
29328
29329     // private
29330     onMouseUp : function(e){
29331         var size = this.resizeElement();
29332         this.resizing = false;
29333         this.handleOut();
29334         this.overlay.hide();
29335         this.proxy.hide();
29336         this.fireEvent("resize", this, size.width, size.height, e);
29337     },
29338
29339     // private
29340     updateChildSize : function(){
29341         
29342         if(this.resizeChild){
29343             var el = this.el;
29344             var child = this.resizeChild;
29345             var adj = this.adjustments;
29346             if(el.dom.offsetWidth){
29347                 var b = el.getSize(true);
29348                 child.setSize(b.width+adj[0], b.height+adj[1]);
29349             }
29350             // Second call here for IE
29351             // The first call enables instant resizing and
29352             // the second call corrects scroll bars if they
29353             // exist
29354             if(Roo.isIE){
29355                 setTimeout(function(){
29356                     if(el.dom.offsetWidth){
29357                         var b = el.getSize(true);
29358                         child.setSize(b.width+adj[0], b.height+adj[1]);
29359                     }
29360                 }, 10);
29361             }
29362         }
29363     },
29364
29365     // private
29366     snap : function(value, inc, min){
29367         if(!inc || !value) return value;
29368         var newValue = value;
29369         var m = value % inc;
29370         if(m > 0){
29371             if(m > (inc/2)){
29372                 newValue = value + (inc-m);
29373             }else{
29374                 newValue = value - m;
29375             }
29376         }
29377         return Math.max(min, newValue);
29378     },
29379
29380     // private
29381     resizeElement : function(){
29382         var box = this.proxy.getBox();
29383         if(this.updateBox){
29384             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29385         }else{
29386             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29387         }
29388         this.updateChildSize();
29389         if(!this.dynamic){
29390             this.proxy.hide();
29391         }
29392         return box;
29393     },
29394
29395     // private
29396     constrain : function(v, diff, m, mx){
29397         if(v - diff < m){
29398             diff = v - m;
29399         }else if(v - diff > mx){
29400             diff = mx - v;
29401         }
29402         return diff;
29403     },
29404
29405     // private
29406     onMouseMove : function(e){
29407         
29408         if(this.enabled){
29409             try{// try catch so if something goes wrong the user doesn't get hung
29410
29411             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29412                 return;
29413             }
29414
29415             //var curXY = this.startPoint;
29416             var curSize = this.curSize || this.startBox;
29417             var x = this.startBox.x, y = this.startBox.y;
29418             var ox = x, oy = y;
29419             var w = curSize.width, h = curSize.height;
29420             var ow = w, oh = h;
29421             var mw = this.minWidth, mh = this.minHeight;
29422             var mxw = this.maxWidth, mxh = this.maxHeight;
29423             var wi = this.widthIncrement;
29424             var hi = this.heightIncrement;
29425
29426             var eventXY = e.getXY();
29427             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29428             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29429
29430             var pos = this.activeHandle.position;
29431
29432             switch(pos){
29433                 case "east":
29434                     w += diffX;
29435                     w = Math.min(Math.max(mw, w), mxw);
29436                     break;
29437              
29438                 case "south":
29439                     h += diffY;
29440                     h = Math.min(Math.max(mh, h), mxh);
29441                     break;
29442                 case "southeast":
29443                     w += diffX;
29444                     h += diffY;
29445                     w = Math.min(Math.max(mw, w), mxw);
29446                     h = Math.min(Math.max(mh, h), mxh);
29447                     break;
29448                 case "north":
29449                     diffY = this.constrain(h, diffY, mh, mxh);
29450                     y += diffY;
29451                     h -= diffY;
29452                     break;
29453                 case "hdrag":
29454                     
29455                     if (wi) {
29456                         var adiffX = Math.abs(diffX);
29457                         var sub = (adiffX % wi); // how much 
29458                         if (sub > (wi/2)) { // far enough to snap
29459                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29460                         } else {
29461                             // remove difference.. 
29462                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29463                         }
29464                     }
29465                     x += diffX;
29466                     x = Math.max(this.minX, x);
29467                     break;
29468                 case "west":
29469                     diffX = this.constrain(w, diffX, mw, mxw);
29470                     x += diffX;
29471                     w -= diffX;
29472                     break;
29473                 case "northeast":
29474                     w += diffX;
29475                     w = Math.min(Math.max(mw, w), mxw);
29476                     diffY = this.constrain(h, diffY, mh, mxh);
29477                     y += diffY;
29478                     h -= diffY;
29479                     break;
29480                 case "northwest":
29481                     diffX = this.constrain(w, diffX, mw, mxw);
29482                     diffY = this.constrain(h, diffY, mh, mxh);
29483                     y += diffY;
29484                     h -= diffY;
29485                     x += diffX;
29486                     w -= diffX;
29487                     break;
29488                case "southwest":
29489                     diffX = this.constrain(w, diffX, mw, mxw);
29490                     h += diffY;
29491                     h = Math.min(Math.max(mh, h), mxh);
29492                     x += diffX;
29493                     w -= diffX;
29494                     break;
29495             }
29496
29497             var sw = this.snap(w, wi, mw);
29498             var sh = this.snap(h, hi, mh);
29499             if(sw != w || sh != h){
29500                 switch(pos){
29501                     case "northeast":
29502                         y -= sh - h;
29503                     break;
29504                     case "north":
29505                         y -= sh - h;
29506                         break;
29507                     case "southwest":
29508                         x -= sw - w;
29509                     break;
29510                     case "west":
29511                         x -= sw - w;
29512                         break;
29513                     case "northwest":
29514                         x -= sw - w;
29515                         y -= sh - h;
29516                     break;
29517                 }
29518                 w = sw;
29519                 h = sh;
29520             }
29521
29522             if(this.preserveRatio){
29523                 switch(pos){
29524                     case "southeast":
29525                     case "east":
29526                         h = oh * (w/ow);
29527                         h = Math.min(Math.max(mh, h), mxh);
29528                         w = ow * (h/oh);
29529                        break;
29530                     case "south":
29531                         w = ow * (h/oh);
29532                         w = Math.min(Math.max(mw, w), mxw);
29533                         h = oh * (w/ow);
29534                         break;
29535                     case "northeast":
29536                         w = ow * (h/oh);
29537                         w = Math.min(Math.max(mw, w), mxw);
29538                         h = oh * (w/ow);
29539                     break;
29540                     case "north":
29541                         var tw = w;
29542                         w = ow * (h/oh);
29543                         w = Math.min(Math.max(mw, w), mxw);
29544                         h = oh * (w/ow);
29545                         x += (tw - w) / 2;
29546                         break;
29547                     case "southwest":
29548                         h = oh * (w/ow);
29549                         h = Math.min(Math.max(mh, h), mxh);
29550                         var tw = w;
29551                         w = ow * (h/oh);
29552                         x += tw - w;
29553                         break;
29554                     case "west":
29555                         var th = h;
29556                         h = oh * (w/ow);
29557                         h = Math.min(Math.max(mh, h), mxh);
29558                         y += (th - h) / 2;
29559                         var tw = w;
29560                         w = ow * (h/oh);
29561                         x += tw - w;
29562                        break;
29563                     case "northwest":
29564                         var tw = w;
29565                         var th = h;
29566                         h = oh * (w/ow);
29567                         h = Math.min(Math.max(mh, h), mxh);
29568                         w = ow * (h/oh);
29569                         y += th - h;
29570                         x += tw - w;
29571                        break;
29572
29573                 }
29574             }
29575             if (pos == 'hdrag') {
29576                 w = ow;
29577             }
29578             this.proxy.setBounds(x, y, w, h);
29579             if(this.dynamic){
29580                 this.resizeElement();
29581             }
29582             }catch(e){}
29583         }
29584         this.fireEvent("resizing", this, x, y, w, h, e);
29585     },
29586
29587     // private
29588     handleOver : function(){
29589         if(this.enabled){
29590             this.el.addClass("x-resizable-over");
29591         }
29592     },
29593
29594     // private
29595     handleOut : function(){
29596         if(!this.resizing){
29597             this.el.removeClass("x-resizable-over");
29598         }
29599     },
29600
29601     /**
29602      * Returns the element this component is bound to.
29603      * @return {Roo.Element}
29604      */
29605     getEl : function(){
29606         return this.el;
29607     },
29608
29609     /**
29610      * Returns the resizeChild element (or null).
29611      * @return {Roo.Element}
29612      */
29613     getResizeChild : function(){
29614         return this.resizeChild;
29615     },
29616     groupHandler : function()
29617     {
29618         
29619     },
29620     /**
29621      * Destroys this resizable. If the element was wrapped and
29622      * removeEl is not true then the element remains.
29623      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29624      */
29625     destroy : function(removeEl){
29626         this.proxy.remove();
29627         if(this.overlay){
29628             this.overlay.removeAllListeners();
29629             this.overlay.remove();
29630         }
29631         var ps = Roo.Resizable.positions;
29632         for(var k in ps){
29633             if(typeof ps[k] != "function" && this[ps[k]]){
29634                 var h = this[ps[k]];
29635                 h.el.removeAllListeners();
29636                 h.el.remove();
29637             }
29638         }
29639         if(removeEl){
29640             this.el.update("");
29641             this.el.remove();
29642         }
29643     }
29644 });
29645
29646 // private
29647 // hash to map config positions to true positions
29648 Roo.Resizable.positions = {
29649     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29650     hd: "hdrag"
29651 };
29652
29653 // private
29654 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29655     if(!this.tpl){
29656         // only initialize the template if resizable is used
29657         var tpl = Roo.DomHelper.createTemplate(
29658             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29659         );
29660         tpl.compile();
29661         Roo.Resizable.Handle.prototype.tpl = tpl;
29662     }
29663     this.position = pos;
29664     this.rz = rz;
29665     // show north drag fro topdra
29666     var handlepos = pos == 'hdrag' ? 'north' : pos;
29667     
29668     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29669     if (pos == 'hdrag') {
29670         this.el.setStyle('cursor', 'pointer');
29671     }
29672     this.el.unselectable();
29673     if(transparent){
29674         this.el.setOpacity(0);
29675     }
29676     this.el.on("mousedown", this.onMouseDown, this);
29677     if(!disableTrackOver){
29678         this.el.on("mouseover", this.onMouseOver, this);
29679         this.el.on("mouseout", this.onMouseOut, this);
29680     }
29681 };
29682
29683 // private
29684 Roo.Resizable.Handle.prototype = {
29685     afterResize : function(rz){
29686         Roo.log('after?');
29687         // do nothing
29688     },
29689     // private
29690     onMouseDown : function(e){
29691         this.rz.onMouseDown(this, e);
29692     },
29693     // private
29694     onMouseOver : function(e){
29695         this.rz.handleOver(this, e);
29696     },
29697     // private
29698     onMouseOut : function(e){
29699         this.rz.handleOut(this, e);
29700     }
29701 };/*
29702  * Based on:
29703  * Ext JS Library 1.1.1
29704  * Copyright(c) 2006-2007, Ext JS, LLC.
29705  *
29706  * Originally Released Under LGPL - original licence link has changed is not relivant.
29707  *
29708  * Fork - LGPL
29709  * <script type="text/javascript">
29710  */
29711
29712 /**
29713  * @class Roo.Editor
29714  * @extends Roo.Component
29715  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29716  * @constructor
29717  * Create a new Editor
29718  * @param {Roo.form.Field} field The Field object (or descendant)
29719  * @param {Object} config The config object
29720  */
29721 Roo.Editor = function(field, config){
29722     Roo.Editor.superclass.constructor.call(this, config);
29723     this.field = field;
29724     this.addEvents({
29725         /**
29726              * @event beforestartedit
29727              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29728              * false from the handler of this event.
29729              * @param {Editor} this
29730              * @param {Roo.Element} boundEl The underlying element bound to this editor
29731              * @param {Mixed} value The field value being set
29732              */
29733         "beforestartedit" : true,
29734         /**
29735              * @event startedit
29736              * Fires when this editor is displayed
29737              * @param {Roo.Element} boundEl The underlying element bound to this editor
29738              * @param {Mixed} value The starting field value
29739              */
29740         "startedit" : true,
29741         /**
29742              * @event beforecomplete
29743              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29744              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29745              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29746              * event will not fire since no edit actually occurred.
29747              * @param {Editor} this
29748              * @param {Mixed} value The current field value
29749              * @param {Mixed} startValue The original field value
29750              */
29751         "beforecomplete" : true,
29752         /**
29753              * @event complete
29754              * Fires after editing is complete and any changed value has been written to the underlying field.
29755              * @param {Editor} this
29756              * @param {Mixed} value The current field value
29757              * @param {Mixed} startValue The original field value
29758              */
29759         "complete" : true,
29760         /**
29761          * @event specialkey
29762          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29763          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29764          * @param {Roo.form.Field} this
29765          * @param {Roo.EventObject} e The event object
29766          */
29767         "specialkey" : true
29768     });
29769 };
29770
29771 Roo.extend(Roo.Editor, Roo.Component, {
29772     /**
29773      * @cfg {Boolean/String} autosize
29774      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29775      * or "height" to adopt the height only (defaults to false)
29776      */
29777     /**
29778      * @cfg {Boolean} revertInvalid
29779      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29780      * validation fails (defaults to true)
29781      */
29782     /**
29783      * @cfg {Boolean} ignoreNoChange
29784      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29785      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29786      * will never be ignored.
29787      */
29788     /**
29789      * @cfg {Boolean} hideEl
29790      * False to keep the bound element visible while the editor is displayed (defaults to true)
29791      */
29792     /**
29793      * @cfg {Mixed} value
29794      * The data value of the underlying field (defaults to "")
29795      */
29796     value : "",
29797     /**
29798      * @cfg {String} alignment
29799      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29800      */
29801     alignment: "c-c?",
29802     /**
29803      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29804      * for bottom-right shadow (defaults to "frame")
29805      */
29806     shadow : "frame",
29807     /**
29808      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29809      */
29810     constrain : false,
29811     /**
29812      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29813      */
29814     completeOnEnter : false,
29815     /**
29816      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29817      */
29818     cancelOnEsc : false,
29819     /**
29820      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29821      */
29822     updateEl : false,
29823
29824     // private
29825     onRender : function(ct, position){
29826         this.el = new Roo.Layer({
29827             shadow: this.shadow,
29828             cls: "x-editor",
29829             parentEl : ct,
29830             shim : this.shim,
29831             shadowOffset:4,
29832             id: this.id,
29833             constrain: this.constrain
29834         });
29835         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29836         if(this.field.msgTarget != 'title'){
29837             this.field.msgTarget = 'qtip';
29838         }
29839         this.field.render(this.el);
29840         if(Roo.isGecko){
29841             this.field.el.dom.setAttribute('autocomplete', 'off');
29842         }
29843         this.field.on("specialkey", this.onSpecialKey, this);
29844         if(this.swallowKeys){
29845             this.field.el.swallowEvent(['keydown','keypress']);
29846         }
29847         this.field.show();
29848         this.field.on("blur", this.onBlur, this);
29849         if(this.field.grow){
29850             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29851         }
29852     },
29853
29854     onSpecialKey : function(field, e)
29855     {
29856         //Roo.log('editor onSpecialKey');
29857         if(this.completeOnEnter && e.getKey() == e.ENTER){
29858             e.stopEvent();
29859             this.completeEdit();
29860             return;
29861         }
29862         // do not fire special key otherwise it might hide close the editor...
29863         if(e.getKey() == e.ENTER){    
29864             return;
29865         }
29866         if(this.cancelOnEsc && e.getKey() == e.ESC){
29867             this.cancelEdit();
29868             return;
29869         } 
29870         this.fireEvent('specialkey', field, e);
29871     
29872     },
29873
29874     /**
29875      * Starts the editing process and shows the editor.
29876      * @param {String/HTMLElement/Element} el The element to edit
29877      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29878       * to the innerHTML of el.
29879      */
29880     startEdit : function(el, value){
29881         if(this.editing){
29882             this.completeEdit();
29883         }
29884         this.boundEl = Roo.get(el);
29885         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29886         if(!this.rendered){
29887             this.render(this.parentEl || document.body);
29888         }
29889         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29890             return;
29891         }
29892         this.startValue = v;
29893         this.field.setValue(v);
29894         if(this.autoSize){
29895             var sz = this.boundEl.getSize();
29896             switch(this.autoSize){
29897                 case "width":
29898                 this.setSize(sz.width,  "");
29899                 break;
29900                 case "height":
29901                 this.setSize("",  sz.height);
29902                 break;
29903                 default:
29904                 this.setSize(sz.width,  sz.height);
29905             }
29906         }
29907         this.el.alignTo(this.boundEl, this.alignment);
29908         this.editing = true;
29909         if(Roo.QuickTips){
29910             Roo.QuickTips.disable();
29911         }
29912         this.show();
29913     },
29914
29915     /**
29916      * Sets the height and width of this editor.
29917      * @param {Number} width The new width
29918      * @param {Number} height The new height
29919      */
29920     setSize : function(w, h){
29921         this.field.setSize(w, h);
29922         if(this.el){
29923             this.el.sync();
29924         }
29925     },
29926
29927     /**
29928      * Realigns the editor to the bound field based on the current alignment config value.
29929      */
29930     realign : function(){
29931         this.el.alignTo(this.boundEl, this.alignment);
29932     },
29933
29934     /**
29935      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29936      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29937      */
29938     completeEdit : function(remainVisible){
29939         if(!this.editing){
29940             return;
29941         }
29942         var v = this.getValue();
29943         if(this.revertInvalid !== false && !this.field.isValid()){
29944             v = this.startValue;
29945             this.cancelEdit(true);
29946         }
29947         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29948             this.editing = false;
29949             this.hide();
29950             return;
29951         }
29952         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29953             this.editing = false;
29954             if(this.updateEl && this.boundEl){
29955                 this.boundEl.update(v);
29956             }
29957             if(remainVisible !== true){
29958                 this.hide();
29959             }
29960             this.fireEvent("complete", this, v, this.startValue);
29961         }
29962     },
29963
29964     // private
29965     onShow : function(){
29966         this.el.show();
29967         if(this.hideEl !== false){
29968             this.boundEl.hide();
29969         }
29970         this.field.show();
29971         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29972             this.fixIEFocus = true;
29973             this.deferredFocus.defer(50, this);
29974         }else{
29975             this.field.focus();
29976         }
29977         this.fireEvent("startedit", this.boundEl, this.startValue);
29978     },
29979
29980     deferredFocus : function(){
29981         if(this.editing){
29982             this.field.focus();
29983         }
29984     },
29985
29986     /**
29987      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29988      * reverted to the original starting value.
29989      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29990      * cancel (defaults to false)
29991      */
29992     cancelEdit : function(remainVisible){
29993         if(this.editing){
29994             this.setValue(this.startValue);
29995             if(remainVisible !== true){
29996                 this.hide();
29997             }
29998         }
29999     },
30000
30001     // private
30002     onBlur : function(){
30003         if(this.allowBlur !== true && this.editing){
30004             this.completeEdit();
30005         }
30006     },
30007
30008     // private
30009     onHide : function(){
30010         if(this.editing){
30011             this.completeEdit();
30012             return;
30013         }
30014         this.field.blur();
30015         if(this.field.collapse){
30016             this.field.collapse();
30017         }
30018         this.el.hide();
30019         if(this.hideEl !== false){
30020             this.boundEl.show();
30021         }
30022         if(Roo.QuickTips){
30023             Roo.QuickTips.enable();
30024         }
30025     },
30026
30027     /**
30028      * Sets the data value of the editor
30029      * @param {Mixed} value Any valid value supported by the underlying field
30030      */
30031     setValue : function(v){
30032         this.field.setValue(v);
30033     },
30034
30035     /**
30036      * Gets the data value of the editor
30037      * @return {Mixed} The data value
30038      */
30039     getValue : function(){
30040         return this.field.getValue();
30041     }
30042 });/*
30043  * Based on:
30044  * Ext JS Library 1.1.1
30045  * Copyright(c) 2006-2007, Ext JS, LLC.
30046  *
30047  * Originally Released Under LGPL - original licence link has changed is not relivant.
30048  *
30049  * Fork - LGPL
30050  * <script type="text/javascript">
30051  */
30052  
30053 /**
30054  * @class Roo.BasicDialog
30055  * @extends Roo.util.Observable
30056  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30057  * <pre><code>
30058 var dlg = new Roo.BasicDialog("my-dlg", {
30059     height: 200,
30060     width: 300,
30061     minHeight: 100,
30062     minWidth: 150,
30063     modal: true,
30064     proxyDrag: true,
30065     shadow: true
30066 });
30067 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30068 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30069 dlg.addButton('Cancel', dlg.hide, dlg);
30070 dlg.show();
30071 </code></pre>
30072   <b>A Dialog should always be a direct child of the body element.</b>
30073  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30074  * @cfg {String} title Default text to display in the title bar (defaults to null)
30075  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30076  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30077  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30078  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30079  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30080  * (defaults to null with no animation)
30081  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30082  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30083  * property for valid values (defaults to 'all')
30084  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30085  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30086  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30087  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30088  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30089  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30090  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30091  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30092  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30093  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30094  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30095  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30096  * draggable = true (defaults to false)
30097  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30098  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30099  * shadow (defaults to false)
30100  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30101  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30102  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30103  * @cfg {Array} buttons Array of buttons
30104  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30105  * @constructor
30106  * Create a new BasicDialog.
30107  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30108  * @param {Object} config Configuration options
30109  */
30110 Roo.BasicDialog = function(el, config){
30111     this.el = Roo.get(el);
30112     var dh = Roo.DomHelper;
30113     if(!this.el && config && config.autoCreate){
30114         if(typeof config.autoCreate == "object"){
30115             if(!config.autoCreate.id){
30116                 config.autoCreate.id = el;
30117             }
30118             this.el = dh.append(document.body,
30119                         config.autoCreate, true);
30120         }else{
30121             this.el = dh.append(document.body,
30122                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30123         }
30124     }
30125     el = this.el;
30126     el.setDisplayed(true);
30127     el.hide = this.hideAction;
30128     this.id = el.id;
30129     el.addClass("x-dlg");
30130
30131     Roo.apply(this, config);
30132
30133     this.proxy = el.createProxy("x-dlg-proxy");
30134     this.proxy.hide = this.hideAction;
30135     this.proxy.setOpacity(.5);
30136     this.proxy.hide();
30137
30138     if(config.width){
30139         el.setWidth(config.width);
30140     }
30141     if(config.height){
30142         el.setHeight(config.height);
30143     }
30144     this.size = el.getSize();
30145     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30146         this.xy = [config.x,config.y];
30147     }else{
30148         this.xy = el.getCenterXY(true);
30149     }
30150     /** The header element @type Roo.Element */
30151     this.header = el.child("> .x-dlg-hd");
30152     /** The body element @type Roo.Element */
30153     this.body = el.child("> .x-dlg-bd");
30154     /** The footer element @type Roo.Element */
30155     this.footer = el.child("> .x-dlg-ft");
30156
30157     if(!this.header){
30158         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30159     }
30160     if(!this.body){
30161         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30162     }
30163
30164     this.header.unselectable();
30165     if(this.title){
30166         this.header.update(this.title);
30167     }
30168     // this element allows the dialog to be focused for keyboard event
30169     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30170     this.focusEl.swallowEvent("click", true);
30171
30172     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30173
30174     // wrap the body and footer for special rendering
30175     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30176     if(this.footer){
30177         this.bwrap.dom.appendChild(this.footer.dom);
30178     }
30179
30180     this.bg = this.el.createChild({
30181         tag: "div", cls:"x-dlg-bg",
30182         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30183     });
30184     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30185
30186
30187     if(this.autoScroll !== false && !this.autoTabs){
30188         this.body.setStyle("overflow", "auto");
30189     }
30190
30191     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30192
30193     if(this.closable !== false){
30194         this.el.addClass("x-dlg-closable");
30195         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30196         this.close.on("click", this.closeClick, this);
30197         this.close.addClassOnOver("x-dlg-close-over");
30198     }
30199     if(this.collapsible !== false){
30200         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30201         this.collapseBtn.on("click", this.collapseClick, this);
30202         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30203         this.header.on("dblclick", this.collapseClick, this);
30204     }
30205     if(this.resizable !== false){
30206         this.el.addClass("x-dlg-resizable");
30207         this.resizer = new Roo.Resizable(el, {
30208             minWidth: this.minWidth || 80,
30209             minHeight:this.minHeight || 80,
30210             handles: this.resizeHandles || "all",
30211             pinned: true
30212         });
30213         this.resizer.on("beforeresize", this.beforeResize, this);
30214         this.resizer.on("resize", this.onResize, this);
30215     }
30216     if(this.draggable !== false){
30217         el.addClass("x-dlg-draggable");
30218         if (!this.proxyDrag) {
30219             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30220         }
30221         else {
30222             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30223         }
30224         dd.setHandleElId(this.header.id);
30225         dd.endDrag = this.endMove.createDelegate(this);
30226         dd.startDrag = this.startMove.createDelegate(this);
30227         dd.onDrag = this.onDrag.createDelegate(this);
30228         dd.scroll = false;
30229         this.dd = dd;
30230     }
30231     if(this.modal){
30232         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30233         this.mask.enableDisplayMode("block");
30234         this.mask.hide();
30235         this.el.addClass("x-dlg-modal");
30236     }
30237     if(this.shadow){
30238         this.shadow = new Roo.Shadow({
30239             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30240             offset : this.shadowOffset
30241         });
30242     }else{
30243         this.shadowOffset = 0;
30244     }
30245     if(Roo.useShims && this.shim !== false){
30246         this.shim = this.el.createShim();
30247         this.shim.hide = this.hideAction;
30248         this.shim.hide();
30249     }else{
30250         this.shim = false;
30251     }
30252     if(this.autoTabs){
30253         this.initTabs();
30254     }
30255     if (this.buttons) { 
30256         var bts= this.buttons;
30257         this.buttons = [];
30258         Roo.each(bts, function(b) {
30259             this.addButton(b);
30260         }, this);
30261     }
30262     
30263     
30264     this.addEvents({
30265         /**
30266          * @event keydown
30267          * Fires when a key is pressed
30268          * @param {Roo.BasicDialog} this
30269          * @param {Roo.EventObject} e
30270          */
30271         "keydown" : true,
30272         /**
30273          * @event move
30274          * Fires when this dialog is moved by the user.
30275          * @param {Roo.BasicDialog} this
30276          * @param {Number} x The new page X
30277          * @param {Number} y The new page Y
30278          */
30279         "move" : true,
30280         /**
30281          * @event resize
30282          * Fires when this dialog is resized by the user.
30283          * @param {Roo.BasicDialog} this
30284          * @param {Number} width The new width
30285          * @param {Number} height The new height
30286          */
30287         "resize" : true,
30288         /**
30289          * @event beforehide
30290          * Fires before this dialog is hidden.
30291          * @param {Roo.BasicDialog} this
30292          */
30293         "beforehide" : true,
30294         /**
30295          * @event hide
30296          * Fires when this dialog is hidden.
30297          * @param {Roo.BasicDialog} this
30298          */
30299         "hide" : true,
30300         /**
30301          * @event beforeshow
30302          * Fires before this dialog is shown.
30303          * @param {Roo.BasicDialog} this
30304          */
30305         "beforeshow" : true,
30306         /**
30307          * @event show
30308          * Fires when this dialog is shown.
30309          * @param {Roo.BasicDialog} this
30310          */
30311         "show" : true
30312     });
30313     el.on("keydown", this.onKeyDown, this);
30314     el.on("mousedown", this.toFront, this);
30315     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30316     this.el.hide();
30317     Roo.DialogManager.register(this);
30318     Roo.BasicDialog.superclass.constructor.call(this);
30319 };
30320
30321 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30322     shadowOffset: Roo.isIE ? 6 : 5,
30323     minHeight: 80,
30324     minWidth: 200,
30325     minButtonWidth: 75,
30326     defaultButton: null,
30327     buttonAlign: "right",
30328     tabTag: 'div',
30329     firstShow: true,
30330
30331     /**
30332      * Sets the dialog title text
30333      * @param {String} text The title text to display
30334      * @return {Roo.BasicDialog} this
30335      */
30336     setTitle : function(text){
30337         this.header.update(text);
30338         return this;
30339     },
30340
30341     // private
30342     closeClick : function(){
30343         this.hide();
30344     },
30345
30346     // private
30347     collapseClick : function(){
30348         this[this.collapsed ? "expand" : "collapse"]();
30349     },
30350
30351     /**
30352      * Collapses the dialog to its minimized state (only the title bar is visible).
30353      * Equivalent to the user clicking the collapse dialog button.
30354      */
30355     collapse : function(){
30356         if(!this.collapsed){
30357             this.collapsed = true;
30358             this.el.addClass("x-dlg-collapsed");
30359             this.restoreHeight = this.el.getHeight();
30360             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30361         }
30362     },
30363
30364     /**
30365      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30366      * clicking the expand dialog button.
30367      */
30368     expand : function(){
30369         if(this.collapsed){
30370             this.collapsed = false;
30371             this.el.removeClass("x-dlg-collapsed");
30372             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30373         }
30374     },
30375
30376     /**
30377      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30378      * @return {Roo.TabPanel} The tabs component
30379      */
30380     initTabs : function(){
30381         var tabs = this.getTabs();
30382         while(tabs.getTab(0)){
30383             tabs.removeTab(0);
30384         }
30385         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30386             var dom = el.dom;
30387             tabs.addTab(Roo.id(dom), dom.title);
30388             dom.title = "";
30389         });
30390         tabs.activate(0);
30391         return tabs;
30392     },
30393
30394     // private
30395     beforeResize : function(){
30396         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30397     },
30398
30399     // private
30400     onResize : function(){
30401         this.refreshSize();
30402         this.syncBodyHeight();
30403         this.adjustAssets();
30404         this.focus();
30405         this.fireEvent("resize", this, this.size.width, this.size.height);
30406     },
30407
30408     // private
30409     onKeyDown : function(e){
30410         if(this.isVisible()){
30411             this.fireEvent("keydown", this, e);
30412         }
30413     },
30414
30415     /**
30416      * Resizes the dialog.
30417      * @param {Number} width
30418      * @param {Number} height
30419      * @return {Roo.BasicDialog} this
30420      */
30421     resizeTo : function(width, height){
30422         this.el.setSize(width, height);
30423         this.size = {width: width, height: height};
30424         this.syncBodyHeight();
30425         if(this.fixedcenter){
30426             this.center();
30427         }
30428         if(this.isVisible()){
30429             this.constrainXY();
30430             this.adjustAssets();
30431         }
30432         this.fireEvent("resize", this, width, height);
30433         return this;
30434     },
30435
30436
30437     /**
30438      * Resizes the dialog to fit the specified content size.
30439      * @param {Number} width
30440      * @param {Number} height
30441      * @return {Roo.BasicDialog} this
30442      */
30443     setContentSize : function(w, h){
30444         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30445         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30446         //if(!this.el.isBorderBox()){
30447             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30448             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30449         //}
30450         if(this.tabs){
30451             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30452             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30453         }
30454         this.resizeTo(w, h);
30455         return this;
30456     },
30457
30458     /**
30459      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30460      * executed in response to a particular key being pressed while the dialog is active.
30461      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30462      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30463      * @param {Function} fn The function to call
30464      * @param {Object} scope (optional) The scope of the function
30465      * @return {Roo.BasicDialog} this
30466      */
30467     addKeyListener : function(key, fn, scope){
30468         var keyCode, shift, ctrl, alt;
30469         if(typeof key == "object" && !(key instanceof Array)){
30470             keyCode = key["key"];
30471             shift = key["shift"];
30472             ctrl = key["ctrl"];
30473             alt = key["alt"];
30474         }else{
30475             keyCode = key;
30476         }
30477         var handler = function(dlg, e){
30478             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30479                 var k = e.getKey();
30480                 if(keyCode instanceof Array){
30481                     for(var i = 0, len = keyCode.length; i < len; i++){
30482                         if(keyCode[i] == k){
30483                           fn.call(scope || window, dlg, k, e);
30484                           return;
30485                         }
30486                     }
30487                 }else{
30488                     if(k == keyCode){
30489                         fn.call(scope || window, dlg, k, e);
30490                     }
30491                 }
30492             }
30493         };
30494         this.on("keydown", handler);
30495         return this;
30496     },
30497
30498     /**
30499      * Returns the TabPanel component (creates it if it doesn't exist).
30500      * Note: If you wish to simply check for the existence of tabs without creating them,
30501      * check for a null 'tabs' property.
30502      * @return {Roo.TabPanel} The tabs component
30503      */
30504     getTabs : function(){
30505         if(!this.tabs){
30506             this.el.addClass("x-dlg-auto-tabs");
30507             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30508             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30509         }
30510         return this.tabs;
30511     },
30512
30513     /**
30514      * Adds a button to the footer section of the dialog.
30515      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30516      * object or a valid Roo.DomHelper element config
30517      * @param {Function} handler The function called when the button is clicked
30518      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30519      * @return {Roo.Button} The new button
30520      */
30521     addButton : function(config, handler, scope){
30522         var dh = Roo.DomHelper;
30523         if(!this.footer){
30524             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30525         }
30526         if(!this.btnContainer){
30527             var tb = this.footer.createChild({
30528
30529                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30530                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30531             }, null, true);
30532             this.btnContainer = tb.firstChild.firstChild.firstChild;
30533         }
30534         var bconfig = {
30535             handler: handler,
30536             scope: scope,
30537             minWidth: this.minButtonWidth,
30538             hideParent:true
30539         };
30540         if(typeof config == "string"){
30541             bconfig.text = config;
30542         }else{
30543             if(config.tag){
30544                 bconfig.dhconfig = config;
30545             }else{
30546                 Roo.apply(bconfig, config);
30547             }
30548         }
30549         var fc = false;
30550         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30551             bconfig.position = Math.max(0, bconfig.position);
30552             fc = this.btnContainer.childNodes[bconfig.position];
30553         }
30554          
30555         var btn = new Roo.Button(
30556             fc ? 
30557                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30558                 : this.btnContainer.appendChild(document.createElement("td")),
30559             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30560             bconfig
30561         );
30562         this.syncBodyHeight();
30563         if(!this.buttons){
30564             /**
30565              * Array of all the buttons that have been added to this dialog via addButton
30566              * @type Array
30567              */
30568             this.buttons = [];
30569         }
30570         this.buttons.push(btn);
30571         return btn;
30572     },
30573
30574     /**
30575      * Sets the default button to be focused when the dialog is displayed.
30576      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30577      * @return {Roo.BasicDialog} this
30578      */
30579     setDefaultButton : function(btn){
30580         this.defaultButton = btn;
30581         return this;
30582     },
30583
30584     // private
30585     getHeaderFooterHeight : function(safe){
30586         var height = 0;
30587         if(this.header){
30588            height += this.header.getHeight();
30589         }
30590         if(this.footer){
30591            var fm = this.footer.getMargins();
30592             height += (this.footer.getHeight()+fm.top+fm.bottom);
30593         }
30594         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30595         height += this.centerBg.getPadding("tb");
30596         return height;
30597     },
30598
30599     // private
30600     syncBodyHeight : function()
30601     {
30602         var bd = this.body, // the text
30603             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30604             bw = this.bwrap;
30605         var height = this.size.height - this.getHeaderFooterHeight(false);
30606         bd.setHeight(height-bd.getMargins("tb"));
30607         var hh = this.header.getHeight();
30608         var h = this.size.height-hh;
30609         cb.setHeight(h);
30610         
30611         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30612         bw.setHeight(h-cb.getPadding("tb"));
30613         
30614         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30615         bd.setWidth(bw.getWidth(true));
30616         if(this.tabs){
30617             this.tabs.syncHeight();
30618             if(Roo.isIE){
30619                 this.tabs.el.repaint();
30620             }
30621         }
30622     },
30623
30624     /**
30625      * Restores the previous state of the dialog if Roo.state is configured.
30626      * @return {Roo.BasicDialog} this
30627      */
30628     restoreState : function(){
30629         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30630         if(box && box.width){
30631             this.xy = [box.x, box.y];
30632             this.resizeTo(box.width, box.height);
30633         }
30634         return this;
30635     },
30636
30637     // private
30638     beforeShow : function(){
30639         this.expand();
30640         if(this.fixedcenter){
30641             this.xy = this.el.getCenterXY(true);
30642         }
30643         if(this.modal){
30644             Roo.get(document.body).addClass("x-body-masked");
30645             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30646             this.mask.show();
30647         }
30648         this.constrainXY();
30649     },
30650
30651     // private
30652     animShow : function(){
30653         var b = Roo.get(this.animateTarget).getBox();
30654         this.proxy.setSize(b.width, b.height);
30655         this.proxy.setLocation(b.x, b.y);
30656         this.proxy.show();
30657         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30658                     true, .35, this.showEl.createDelegate(this));
30659     },
30660
30661     /**
30662      * Shows the dialog.
30663      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30664      * @return {Roo.BasicDialog} this
30665      */
30666     show : function(animateTarget){
30667         if (this.fireEvent("beforeshow", this) === false){
30668             return;
30669         }
30670         if(this.syncHeightBeforeShow){
30671             this.syncBodyHeight();
30672         }else if(this.firstShow){
30673             this.firstShow = false;
30674             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30675         }
30676         this.animateTarget = animateTarget || this.animateTarget;
30677         if(!this.el.isVisible()){
30678             this.beforeShow();
30679             if(this.animateTarget && Roo.get(this.animateTarget)){
30680                 this.animShow();
30681             }else{
30682                 this.showEl();
30683             }
30684         }
30685         return this;
30686     },
30687
30688     // private
30689     showEl : function(){
30690         this.proxy.hide();
30691         this.el.setXY(this.xy);
30692         this.el.show();
30693         this.adjustAssets(true);
30694         this.toFront();
30695         this.focus();
30696         // IE peekaboo bug - fix found by Dave Fenwick
30697         if(Roo.isIE){
30698             this.el.repaint();
30699         }
30700         this.fireEvent("show", this);
30701     },
30702
30703     /**
30704      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30705      * dialog itself will receive focus.
30706      */
30707     focus : function(){
30708         if(this.defaultButton){
30709             this.defaultButton.focus();
30710         }else{
30711             this.focusEl.focus();
30712         }
30713     },
30714
30715     // private
30716     constrainXY : function(){
30717         if(this.constraintoviewport !== false){
30718             if(!this.viewSize){
30719                 if(this.container){
30720                     var s = this.container.getSize();
30721                     this.viewSize = [s.width, s.height];
30722                 }else{
30723                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30724                 }
30725             }
30726             var s = Roo.get(this.container||document).getScroll();
30727
30728             var x = this.xy[0], y = this.xy[1];
30729             var w = this.size.width, h = this.size.height;
30730             var vw = this.viewSize[0], vh = this.viewSize[1];
30731             // only move it if it needs it
30732             var moved = false;
30733             // first validate right/bottom
30734             if(x + w > vw+s.left){
30735                 x = vw - w;
30736                 moved = true;
30737             }
30738             if(y + h > vh+s.top){
30739                 y = vh - h;
30740                 moved = true;
30741             }
30742             // then make sure top/left isn't negative
30743             if(x < s.left){
30744                 x = s.left;
30745                 moved = true;
30746             }
30747             if(y < s.top){
30748                 y = s.top;
30749                 moved = true;
30750             }
30751             if(moved){
30752                 // cache xy
30753                 this.xy = [x, y];
30754                 if(this.isVisible()){
30755                     this.el.setLocation(x, y);
30756                     this.adjustAssets();
30757                 }
30758             }
30759         }
30760     },
30761
30762     // private
30763     onDrag : function(){
30764         if(!this.proxyDrag){
30765             this.xy = this.el.getXY();
30766             this.adjustAssets();
30767         }
30768     },
30769
30770     // private
30771     adjustAssets : function(doShow){
30772         var x = this.xy[0], y = this.xy[1];
30773         var w = this.size.width, h = this.size.height;
30774         if(doShow === true){
30775             if(this.shadow){
30776                 this.shadow.show(this.el);
30777             }
30778             if(this.shim){
30779                 this.shim.show();
30780             }
30781         }
30782         if(this.shadow && this.shadow.isVisible()){
30783             this.shadow.show(this.el);
30784         }
30785         if(this.shim && this.shim.isVisible()){
30786             this.shim.setBounds(x, y, w, h);
30787         }
30788     },
30789
30790     // private
30791     adjustViewport : function(w, h){
30792         if(!w || !h){
30793             w = Roo.lib.Dom.getViewWidth();
30794             h = Roo.lib.Dom.getViewHeight();
30795         }
30796         // cache the size
30797         this.viewSize = [w, h];
30798         if(this.modal && this.mask.isVisible()){
30799             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30800             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30801         }
30802         if(this.isVisible()){
30803             this.constrainXY();
30804         }
30805     },
30806
30807     /**
30808      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30809      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30810      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30811      */
30812     destroy : function(removeEl){
30813         if(this.isVisible()){
30814             this.animateTarget = null;
30815             this.hide();
30816         }
30817         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30818         if(this.tabs){
30819             this.tabs.destroy(removeEl);
30820         }
30821         Roo.destroy(
30822              this.shim,
30823              this.proxy,
30824              this.resizer,
30825              this.close,
30826              this.mask
30827         );
30828         if(this.dd){
30829             this.dd.unreg();
30830         }
30831         if(this.buttons){
30832            for(var i = 0, len = this.buttons.length; i < len; i++){
30833                this.buttons[i].destroy();
30834            }
30835         }
30836         this.el.removeAllListeners();
30837         if(removeEl === true){
30838             this.el.update("");
30839             this.el.remove();
30840         }
30841         Roo.DialogManager.unregister(this);
30842     },
30843
30844     // private
30845     startMove : function(){
30846         if(this.proxyDrag){
30847             this.proxy.show();
30848         }
30849         if(this.constraintoviewport !== false){
30850             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30851         }
30852     },
30853
30854     // private
30855     endMove : function(){
30856         if(!this.proxyDrag){
30857             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30858         }else{
30859             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30860             this.proxy.hide();
30861         }
30862         this.refreshSize();
30863         this.adjustAssets();
30864         this.focus();
30865         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30866     },
30867
30868     /**
30869      * Brings this dialog to the front of any other visible dialogs
30870      * @return {Roo.BasicDialog} this
30871      */
30872     toFront : function(){
30873         Roo.DialogManager.bringToFront(this);
30874         return this;
30875     },
30876
30877     /**
30878      * Sends this dialog to the back (under) of any other visible dialogs
30879      * @return {Roo.BasicDialog} this
30880      */
30881     toBack : function(){
30882         Roo.DialogManager.sendToBack(this);
30883         return this;
30884     },
30885
30886     /**
30887      * Centers this dialog in the viewport
30888      * @return {Roo.BasicDialog} this
30889      */
30890     center : function(){
30891         var xy = this.el.getCenterXY(true);
30892         this.moveTo(xy[0], xy[1]);
30893         return this;
30894     },
30895
30896     /**
30897      * Moves the dialog's top-left corner to the specified point
30898      * @param {Number} x
30899      * @param {Number} y
30900      * @return {Roo.BasicDialog} this
30901      */
30902     moveTo : function(x, y){
30903         this.xy = [x,y];
30904         if(this.isVisible()){
30905             this.el.setXY(this.xy);
30906             this.adjustAssets();
30907         }
30908         return this;
30909     },
30910
30911     /**
30912      * Aligns the dialog to the specified element
30913      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30914      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30915      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30916      * @return {Roo.BasicDialog} this
30917      */
30918     alignTo : function(element, position, offsets){
30919         this.xy = this.el.getAlignToXY(element, position, offsets);
30920         if(this.isVisible()){
30921             this.el.setXY(this.xy);
30922             this.adjustAssets();
30923         }
30924         return this;
30925     },
30926
30927     /**
30928      * Anchors an element to another element and realigns it when the window is resized.
30929      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30930      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30931      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30932      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30933      * is a number, it is used as the buffer delay (defaults to 50ms).
30934      * @return {Roo.BasicDialog} this
30935      */
30936     anchorTo : function(el, alignment, offsets, monitorScroll){
30937         var action = function(){
30938             this.alignTo(el, alignment, offsets);
30939         };
30940         Roo.EventManager.onWindowResize(action, this);
30941         var tm = typeof monitorScroll;
30942         if(tm != 'undefined'){
30943             Roo.EventManager.on(window, 'scroll', action, this,
30944                 {buffer: tm == 'number' ? monitorScroll : 50});
30945         }
30946         action.call(this);
30947         return this;
30948     },
30949
30950     /**
30951      * Returns true if the dialog is visible
30952      * @return {Boolean}
30953      */
30954     isVisible : function(){
30955         return this.el.isVisible();
30956     },
30957
30958     // private
30959     animHide : function(callback){
30960         var b = Roo.get(this.animateTarget).getBox();
30961         this.proxy.show();
30962         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30963         this.el.hide();
30964         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30965                     this.hideEl.createDelegate(this, [callback]));
30966     },
30967
30968     /**
30969      * Hides the dialog.
30970      * @param {Function} callback (optional) Function to call when the dialog is hidden
30971      * @return {Roo.BasicDialog} this
30972      */
30973     hide : function(callback){
30974         if (this.fireEvent("beforehide", this) === false){
30975             return;
30976         }
30977         if(this.shadow){
30978             this.shadow.hide();
30979         }
30980         if(this.shim) {
30981           this.shim.hide();
30982         }
30983         // sometimes animateTarget seems to get set.. causing problems...
30984         // this just double checks..
30985         if(this.animateTarget && Roo.get(this.animateTarget)) {
30986            this.animHide(callback);
30987         }else{
30988             this.el.hide();
30989             this.hideEl(callback);
30990         }
30991         return this;
30992     },
30993
30994     // private
30995     hideEl : function(callback){
30996         this.proxy.hide();
30997         if(this.modal){
30998             this.mask.hide();
30999             Roo.get(document.body).removeClass("x-body-masked");
31000         }
31001         this.fireEvent("hide", this);
31002         if(typeof callback == "function"){
31003             callback();
31004         }
31005     },
31006
31007     // private
31008     hideAction : function(){
31009         this.setLeft("-10000px");
31010         this.setTop("-10000px");
31011         this.setStyle("visibility", "hidden");
31012     },
31013
31014     // private
31015     refreshSize : function(){
31016         this.size = this.el.getSize();
31017         this.xy = this.el.getXY();
31018         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31019     },
31020
31021     // private
31022     // z-index is managed by the DialogManager and may be overwritten at any time
31023     setZIndex : function(index){
31024         if(this.modal){
31025             this.mask.setStyle("z-index", index);
31026         }
31027         if(this.shim){
31028             this.shim.setStyle("z-index", ++index);
31029         }
31030         if(this.shadow){
31031             this.shadow.setZIndex(++index);
31032         }
31033         this.el.setStyle("z-index", ++index);
31034         if(this.proxy){
31035             this.proxy.setStyle("z-index", ++index);
31036         }
31037         if(this.resizer){
31038             this.resizer.proxy.setStyle("z-index", ++index);
31039         }
31040
31041         this.lastZIndex = index;
31042     },
31043
31044     /**
31045      * Returns the element for this dialog
31046      * @return {Roo.Element} The underlying dialog Element
31047      */
31048     getEl : function(){
31049         return this.el;
31050     }
31051 });
31052
31053 /**
31054  * @class Roo.DialogManager
31055  * Provides global access to BasicDialogs that have been created and
31056  * support for z-indexing (layering) multiple open dialogs.
31057  */
31058 Roo.DialogManager = function(){
31059     var list = {};
31060     var accessList = [];
31061     var front = null;
31062
31063     // private
31064     var sortDialogs = function(d1, d2){
31065         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31066     };
31067
31068     // private
31069     var orderDialogs = function(){
31070         accessList.sort(sortDialogs);
31071         var seed = Roo.DialogManager.zseed;
31072         for(var i = 0, len = accessList.length; i < len; i++){
31073             var dlg = accessList[i];
31074             if(dlg){
31075                 dlg.setZIndex(seed + (i*10));
31076             }
31077         }
31078     };
31079
31080     return {
31081         /**
31082          * The starting z-index for BasicDialogs (defaults to 9000)
31083          * @type Number The z-index value
31084          */
31085         zseed : 9000,
31086
31087         // private
31088         register : function(dlg){
31089             list[dlg.id] = dlg;
31090             accessList.push(dlg);
31091         },
31092
31093         // private
31094         unregister : function(dlg){
31095             delete list[dlg.id];
31096             var i=0;
31097             var len=0;
31098             if(!accessList.indexOf){
31099                 for(  i = 0, len = accessList.length; i < len; i++){
31100                     if(accessList[i] == dlg){
31101                         accessList.splice(i, 1);
31102                         return;
31103                     }
31104                 }
31105             }else{
31106                  i = accessList.indexOf(dlg);
31107                 if(i != -1){
31108                     accessList.splice(i, 1);
31109                 }
31110             }
31111         },
31112
31113         /**
31114          * Gets a registered dialog by id
31115          * @param {String/Object} id The id of the dialog or a dialog
31116          * @return {Roo.BasicDialog} this
31117          */
31118         get : function(id){
31119             return typeof id == "object" ? id : list[id];
31120         },
31121
31122         /**
31123          * Brings the specified dialog to the front
31124          * @param {String/Object} dlg The id of the dialog or a dialog
31125          * @return {Roo.BasicDialog} this
31126          */
31127         bringToFront : function(dlg){
31128             dlg = this.get(dlg);
31129             if(dlg != front){
31130                 front = dlg;
31131                 dlg._lastAccess = new Date().getTime();
31132                 orderDialogs();
31133             }
31134             return dlg;
31135         },
31136
31137         /**
31138          * Sends the specified dialog to the back
31139          * @param {String/Object} dlg The id of the dialog or a dialog
31140          * @return {Roo.BasicDialog} this
31141          */
31142         sendToBack : function(dlg){
31143             dlg = this.get(dlg);
31144             dlg._lastAccess = -(new Date().getTime());
31145             orderDialogs();
31146             return dlg;
31147         },
31148
31149         /**
31150          * Hides all dialogs
31151          */
31152         hideAll : function(){
31153             for(var id in list){
31154                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31155                     list[id].hide();
31156                 }
31157             }
31158         }
31159     };
31160 }();
31161
31162 /**
31163  * @class Roo.LayoutDialog
31164  * @extends Roo.BasicDialog
31165  * Dialog which provides adjustments for working with a layout in a Dialog.
31166  * Add your necessary layout config options to the dialog's config.<br>
31167  * Example usage (including a nested layout):
31168  * <pre><code>
31169 if(!dialog){
31170     dialog = new Roo.LayoutDialog("download-dlg", {
31171         modal: true,
31172         width:600,
31173         height:450,
31174         shadow:true,
31175         minWidth:500,
31176         minHeight:350,
31177         autoTabs:true,
31178         proxyDrag:true,
31179         // layout config merges with the dialog config
31180         center:{
31181             tabPosition: "top",
31182             alwaysShowTabs: true
31183         }
31184     });
31185     dialog.addKeyListener(27, dialog.hide, dialog);
31186     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31187     dialog.addButton("Build It!", this.getDownload, this);
31188
31189     // we can even add nested layouts
31190     var innerLayout = new Roo.BorderLayout("dl-inner", {
31191         east: {
31192             initialSize: 200,
31193             autoScroll:true,
31194             split:true
31195         },
31196         center: {
31197             autoScroll:true
31198         }
31199     });
31200     innerLayout.beginUpdate();
31201     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31202     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31203     innerLayout.endUpdate(true);
31204
31205     var layout = dialog.getLayout();
31206     layout.beginUpdate();
31207     layout.add("center", new Roo.ContentPanel("standard-panel",
31208                         {title: "Download the Source", fitToFrame:true}));
31209     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31210                {title: "Build your own roo.js"}));
31211     layout.getRegion("center").showPanel(sp);
31212     layout.endUpdate();
31213 }
31214 </code></pre>
31215     * @constructor
31216     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31217     * @param {Object} config configuration options
31218   */
31219 Roo.LayoutDialog = function(el, cfg){
31220     
31221     var config=  cfg;
31222     if (typeof(cfg) == 'undefined') {
31223         config = Roo.apply({}, el);
31224         // not sure why we use documentElement here.. - it should always be body.
31225         // IE7 borks horribly if we use documentElement.
31226         // webkit also does not like documentElement - it creates a body element...
31227         el = Roo.get( document.body || document.documentElement ).createChild();
31228         //config.autoCreate = true;
31229     }
31230     
31231     
31232     config.autoTabs = false;
31233     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31234     this.body.setStyle({overflow:"hidden", position:"relative"});
31235     this.layout = new Roo.BorderLayout(this.body.dom, config);
31236     this.layout.monitorWindowResize = false;
31237     this.el.addClass("x-dlg-auto-layout");
31238     // fix case when center region overwrites center function
31239     this.center = Roo.BasicDialog.prototype.center;
31240     this.on("show", this.layout.layout, this.layout, true);
31241     if (config.items) {
31242         var xitems = config.items;
31243         delete config.items;
31244         Roo.each(xitems, this.addxtype, this);
31245     }
31246     
31247     
31248 };
31249 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31250     /**
31251      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31252      * @deprecated
31253      */
31254     endUpdate : function(){
31255         this.layout.endUpdate();
31256     },
31257
31258     /**
31259      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31260      *  @deprecated
31261      */
31262     beginUpdate : function(){
31263         this.layout.beginUpdate();
31264     },
31265
31266     /**
31267      * Get the BorderLayout for this dialog
31268      * @return {Roo.BorderLayout}
31269      */
31270     getLayout : function(){
31271         return this.layout;
31272     },
31273
31274     showEl : function(){
31275         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31276         if(Roo.isIE7){
31277             this.layout.layout();
31278         }
31279     },
31280
31281     // private
31282     // Use the syncHeightBeforeShow config option to control this automatically
31283     syncBodyHeight : function(){
31284         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31285         if(this.layout){this.layout.layout();}
31286     },
31287     
31288       /**
31289      * Add an xtype element (actually adds to the layout.)
31290      * @return {Object} xdata xtype object data.
31291      */
31292     
31293     addxtype : function(c) {
31294         return this.layout.addxtype(c);
31295     }
31296 });/*
31297  * Based on:
31298  * Ext JS Library 1.1.1
31299  * Copyright(c) 2006-2007, Ext JS, LLC.
31300  *
31301  * Originally Released Under LGPL - original licence link has changed is not relivant.
31302  *
31303  * Fork - LGPL
31304  * <script type="text/javascript">
31305  */
31306  
31307 /**
31308  * @class Roo.MessageBox
31309  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31310  * Example usage:
31311  *<pre><code>
31312 // Basic alert:
31313 Roo.Msg.alert('Status', 'Changes saved successfully.');
31314
31315 // Prompt for user data:
31316 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31317     if (btn == 'ok'){
31318         // process text value...
31319     }
31320 });
31321
31322 // Show a dialog using config options:
31323 Roo.Msg.show({
31324    title:'Save Changes?',
31325    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31326    buttons: Roo.Msg.YESNOCANCEL,
31327    fn: processResult,
31328    animEl: 'elId'
31329 });
31330 </code></pre>
31331  * @singleton
31332  */
31333 Roo.MessageBox = function(){
31334     var dlg, opt, mask, waitTimer;
31335     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31336     var buttons, activeTextEl, bwidth;
31337
31338     // private
31339     var handleButton = function(button){
31340         dlg.hide();
31341         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31342     };
31343
31344     // private
31345     var handleHide = function(){
31346         if(opt && opt.cls){
31347             dlg.el.removeClass(opt.cls);
31348         }
31349         if(waitTimer){
31350             Roo.TaskMgr.stop(waitTimer);
31351             waitTimer = null;
31352         }
31353     };
31354
31355     // private
31356     var updateButtons = function(b){
31357         var width = 0;
31358         if(!b){
31359             buttons["ok"].hide();
31360             buttons["cancel"].hide();
31361             buttons["yes"].hide();
31362             buttons["no"].hide();
31363             dlg.footer.dom.style.display = 'none';
31364             return width;
31365         }
31366         dlg.footer.dom.style.display = '';
31367         for(var k in buttons){
31368             if(typeof buttons[k] != "function"){
31369                 if(b[k]){
31370                     buttons[k].show();
31371                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31372                     width += buttons[k].el.getWidth()+15;
31373                 }else{
31374                     buttons[k].hide();
31375                 }
31376             }
31377         }
31378         return width;
31379     };
31380
31381     // private
31382     var handleEsc = function(d, k, e){
31383         if(opt && opt.closable !== false){
31384             dlg.hide();
31385         }
31386         if(e){
31387             e.stopEvent();
31388         }
31389     };
31390
31391     return {
31392         /**
31393          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31394          * @return {Roo.BasicDialog} The BasicDialog element
31395          */
31396         getDialog : function(){
31397            if(!dlg){
31398                 dlg = new Roo.BasicDialog("x-msg-box", {
31399                     autoCreate : true,
31400                     shadow: true,
31401                     draggable: true,
31402                     resizable:false,
31403                     constraintoviewport:false,
31404                     fixedcenter:true,
31405                     collapsible : false,
31406                     shim:true,
31407                     modal: true,
31408                     width:400, height:100,
31409                     buttonAlign:"center",
31410                     closeClick : function(){
31411                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31412                             handleButton("no");
31413                         }else{
31414                             handleButton("cancel");
31415                         }
31416                     }
31417                 });
31418                 dlg.on("hide", handleHide);
31419                 mask = dlg.mask;
31420                 dlg.addKeyListener(27, handleEsc);
31421                 buttons = {};
31422                 var bt = this.buttonText;
31423                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31424                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31425                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31426                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31427                 bodyEl = dlg.body.createChild({
31428
31429                     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>'
31430                 });
31431                 msgEl = bodyEl.dom.firstChild;
31432                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31433                 textboxEl.enableDisplayMode();
31434                 textboxEl.addKeyListener([10,13], function(){
31435                     if(dlg.isVisible() && opt && opt.buttons){
31436                         if(opt.buttons.ok){
31437                             handleButton("ok");
31438                         }else if(opt.buttons.yes){
31439                             handleButton("yes");
31440                         }
31441                     }
31442                 });
31443                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31444                 textareaEl.enableDisplayMode();
31445                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31446                 progressEl.enableDisplayMode();
31447                 var pf = progressEl.dom.firstChild;
31448                 if (pf) {
31449                     pp = Roo.get(pf.firstChild);
31450                     pp.setHeight(pf.offsetHeight);
31451                 }
31452                 
31453             }
31454             return dlg;
31455         },
31456
31457         /**
31458          * Updates the message box body text
31459          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31460          * the XHTML-compliant non-breaking space character '&amp;#160;')
31461          * @return {Roo.MessageBox} This message box
31462          */
31463         updateText : function(text){
31464             if(!dlg.isVisible() && !opt.width){
31465                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31466             }
31467             msgEl.innerHTML = text || '&#160;';
31468       
31469             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31470             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31471             var w = Math.max(
31472                     Math.min(opt.width || cw , this.maxWidth), 
31473                     Math.max(opt.minWidth || this.minWidth, bwidth)
31474             );
31475             if(opt.prompt){
31476                 activeTextEl.setWidth(w);
31477             }
31478             if(dlg.isVisible()){
31479                 dlg.fixedcenter = false;
31480             }
31481             // to big, make it scroll. = But as usual stupid IE does not support
31482             // !important..
31483             
31484             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31485                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31486                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31487             } else {
31488                 bodyEl.dom.style.height = '';
31489                 bodyEl.dom.style.overflowY = '';
31490             }
31491             if (cw > w) {
31492                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31493             } else {
31494                 bodyEl.dom.style.overflowX = '';
31495             }
31496             
31497             dlg.setContentSize(w, bodyEl.getHeight());
31498             if(dlg.isVisible()){
31499                 dlg.fixedcenter = true;
31500             }
31501             return this;
31502         },
31503
31504         /**
31505          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31506          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31507          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31508          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31509          * @return {Roo.MessageBox} This message box
31510          */
31511         updateProgress : function(value, text){
31512             if(text){
31513                 this.updateText(text);
31514             }
31515             if (pp) { // weird bug on my firefox - for some reason this is not defined
31516                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31517             }
31518             return this;
31519         },        
31520
31521         /**
31522          * Returns true if the message box is currently displayed
31523          * @return {Boolean} True if the message box is visible, else false
31524          */
31525         isVisible : function(){
31526             return dlg && dlg.isVisible();  
31527         },
31528
31529         /**
31530          * Hides the message box if it is displayed
31531          */
31532         hide : function(){
31533             if(this.isVisible()){
31534                 dlg.hide();
31535             }  
31536         },
31537
31538         /**
31539          * Displays a new message box, or reinitializes an existing message box, based on the config options
31540          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31541          * The following config object properties are supported:
31542          * <pre>
31543 Property    Type             Description
31544 ----------  ---------------  ------------------------------------------------------------------------------------
31545 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31546                                    closes (defaults to undefined)
31547 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31548                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31549 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31550                                    progress and wait dialogs will ignore this property and always hide the
31551                                    close button as they can only be closed programmatically.
31552 cls               String           A custom CSS class to apply to the message box element
31553 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31554                                    displayed (defaults to 75)
31555 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31556                                    function will be btn (the name of the button that was clicked, if applicable,
31557                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31558                                    Progress and wait dialogs will ignore this option since they do not respond to
31559                                    user actions and can only be closed programmatically, so any required function
31560                                    should be called by the same code after it closes the dialog.
31561 icon              String           A CSS class that provides a background image to be used as an icon for
31562                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31563 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31564 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31565 modal             Boolean          False to allow user interaction with the page while the message box is
31566                                    displayed (defaults to true)
31567 msg               String           A string that will replace the existing message box body text (defaults
31568                                    to the XHTML-compliant non-breaking space character '&#160;')
31569 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31570 progress          Boolean          True to display a progress bar (defaults to false)
31571 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31572 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31573 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31574 title             String           The title text
31575 value             String           The string value to set into the active textbox element if displayed
31576 wait              Boolean          True to display a progress bar (defaults to false)
31577 width             Number           The width of the dialog in pixels
31578 </pre>
31579          *
31580          * Example usage:
31581          * <pre><code>
31582 Roo.Msg.show({
31583    title: 'Address',
31584    msg: 'Please enter your address:',
31585    width: 300,
31586    buttons: Roo.MessageBox.OKCANCEL,
31587    multiline: true,
31588    fn: saveAddress,
31589    animEl: 'addAddressBtn'
31590 });
31591 </code></pre>
31592          * @param {Object} config Configuration options
31593          * @return {Roo.MessageBox} This message box
31594          */
31595         show : function(options)
31596         {
31597             
31598             // this causes nightmares if you show one dialog after another
31599             // especially on callbacks..
31600              
31601             if(this.isVisible()){
31602                 
31603                 this.hide();
31604                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31605                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31606                 Roo.log("New Dialog Message:" +  options.msg )
31607                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31608                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31609                 
31610             }
31611             var d = this.getDialog();
31612             opt = options;
31613             d.setTitle(opt.title || "&#160;");
31614             d.close.setDisplayed(opt.closable !== false);
31615             activeTextEl = textboxEl;
31616             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31617             if(opt.prompt){
31618                 if(opt.multiline){
31619                     textboxEl.hide();
31620                     textareaEl.show();
31621                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31622                         opt.multiline : this.defaultTextHeight);
31623                     activeTextEl = textareaEl;
31624                 }else{
31625                     textboxEl.show();
31626                     textareaEl.hide();
31627                 }
31628             }else{
31629                 textboxEl.hide();
31630                 textareaEl.hide();
31631             }
31632             progressEl.setDisplayed(opt.progress === true);
31633             this.updateProgress(0);
31634             activeTextEl.dom.value = opt.value || "";
31635             if(opt.prompt){
31636                 dlg.setDefaultButton(activeTextEl);
31637             }else{
31638                 var bs = opt.buttons;
31639                 var db = null;
31640                 if(bs && bs.ok){
31641                     db = buttons["ok"];
31642                 }else if(bs && bs.yes){
31643                     db = buttons["yes"];
31644                 }
31645                 dlg.setDefaultButton(db);
31646             }
31647             bwidth = updateButtons(opt.buttons);
31648             this.updateText(opt.msg);
31649             if(opt.cls){
31650                 d.el.addClass(opt.cls);
31651             }
31652             d.proxyDrag = opt.proxyDrag === true;
31653             d.modal = opt.modal !== false;
31654             d.mask = opt.modal !== false ? mask : false;
31655             if(!d.isVisible()){
31656                 // force it to the end of the z-index stack so it gets a cursor in FF
31657                 document.body.appendChild(dlg.el.dom);
31658                 d.animateTarget = null;
31659                 d.show(options.animEl);
31660             }
31661             return this;
31662         },
31663
31664         /**
31665          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31666          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31667          * and closing the message box when the process is complete.
31668          * @param {String} title The title bar text
31669          * @param {String} msg The message box body text
31670          * @return {Roo.MessageBox} This message box
31671          */
31672         progress : function(title, msg){
31673             this.show({
31674                 title : title,
31675                 msg : msg,
31676                 buttons: false,
31677                 progress:true,
31678                 closable:false,
31679                 minWidth: this.minProgressWidth,
31680                 modal : true
31681             });
31682             return this;
31683         },
31684
31685         /**
31686          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31687          * If a callback function is passed it will be called after the user clicks the button, and the
31688          * id of the button that was clicked will be passed as the only parameter to the callback
31689          * (could also be the top-right close button).
31690          * @param {String} title The title bar text
31691          * @param {String} msg The message box body text
31692          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31693          * @param {Object} scope (optional) The scope of the callback function
31694          * @return {Roo.MessageBox} This message box
31695          */
31696         alert : function(title, msg, fn, scope){
31697             this.show({
31698                 title : title,
31699                 msg : msg,
31700                 buttons: this.OK,
31701                 fn: fn,
31702                 scope : scope,
31703                 modal : true
31704             });
31705             return this;
31706         },
31707
31708         /**
31709          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31710          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31711          * You are responsible for closing the message box when the process is complete.
31712          * @param {String} msg The message box body text
31713          * @param {String} title (optional) The title bar text
31714          * @return {Roo.MessageBox} This message box
31715          */
31716         wait : function(msg, title){
31717             this.show({
31718                 title : title,
31719                 msg : msg,
31720                 buttons: false,
31721                 closable:false,
31722                 progress:true,
31723                 modal:true,
31724                 width:300,
31725                 wait:true
31726             });
31727             waitTimer = Roo.TaskMgr.start({
31728                 run: function(i){
31729                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31730                 },
31731                 interval: 1000
31732             });
31733             return this;
31734         },
31735
31736         /**
31737          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31738          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31739          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31740          * @param {String} title The title bar text
31741          * @param {String} msg The message box body text
31742          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31743          * @param {Object} scope (optional) The scope of the callback function
31744          * @return {Roo.MessageBox} This message box
31745          */
31746         confirm : function(title, msg, fn, scope){
31747             this.show({
31748                 title : title,
31749                 msg : msg,
31750                 buttons: this.YESNO,
31751                 fn: fn,
31752                 scope : scope,
31753                 modal : true
31754             });
31755             return this;
31756         },
31757
31758         /**
31759          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31760          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31761          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31762          * (could also be the top-right close button) and the text that was entered will be passed as the two
31763          * parameters to the callback.
31764          * @param {String} title The title bar text
31765          * @param {String} msg The message box body text
31766          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31767          * @param {Object} scope (optional) The scope of the callback function
31768          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31769          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31770          * @return {Roo.MessageBox} This message box
31771          */
31772         prompt : function(title, msg, fn, scope, multiline){
31773             this.show({
31774                 title : title,
31775                 msg : msg,
31776                 buttons: this.OKCANCEL,
31777                 fn: fn,
31778                 minWidth:250,
31779                 scope : scope,
31780                 prompt:true,
31781                 multiline: multiline,
31782                 modal : true
31783             });
31784             return this;
31785         },
31786
31787         /**
31788          * Button config that displays a single OK button
31789          * @type Object
31790          */
31791         OK : {ok:true},
31792         /**
31793          * Button config that displays Yes and No buttons
31794          * @type Object
31795          */
31796         YESNO : {yes:true, no:true},
31797         /**
31798          * Button config that displays OK and Cancel buttons
31799          * @type Object
31800          */
31801         OKCANCEL : {ok:true, cancel:true},
31802         /**
31803          * Button config that displays Yes, No and Cancel buttons
31804          * @type Object
31805          */
31806         YESNOCANCEL : {yes:true, no:true, cancel:true},
31807
31808         /**
31809          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31810          * @type Number
31811          */
31812         defaultTextHeight : 75,
31813         /**
31814          * The maximum width in pixels of the message box (defaults to 600)
31815          * @type Number
31816          */
31817         maxWidth : 600,
31818         /**
31819          * The minimum width in pixels of the message box (defaults to 100)
31820          * @type Number
31821          */
31822         minWidth : 100,
31823         /**
31824          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31825          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31826          * @type Number
31827          */
31828         minProgressWidth : 250,
31829         /**
31830          * An object containing the default button text strings that can be overriden for localized language support.
31831          * Supported properties are: ok, cancel, yes and no.
31832          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31833          * @type Object
31834          */
31835         buttonText : {
31836             ok : "OK",
31837             cancel : "Cancel",
31838             yes : "Yes",
31839             no : "No"
31840         }
31841     };
31842 }();
31843
31844 /**
31845  * Shorthand for {@link Roo.MessageBox}
31846  */
31847 Roo.Msg = Roo.MessageBox;/*
31848  * Based on:
31849  * Ext JS Library 1.1.1
31850  * Copyright(c) 2006-2007, Ext JS, LLC.
31851  *
31852  * Originally Released Under LGPL - original licence link has changed is not relivant.
31853  *
31854  * Fork - LGPL
31855  * <script type="text/javascript">
31856  */
31857 /**
31858  * @class Roo.QuickTips
31859  * Provides attractive and customizable tooltips for any element.
31860  * @singleton
31861  */
31862 Roo.QuickTips = function(){
31863     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31864     var ce, bd, xy, dd;
31865     var visible = false, disabled = true, inited = false;
31866     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31867     
31868     var onOver = function(e){
31869         if(disabled){
31870             return;
31871         }
31872         var t = e.getTarget();
31873         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31874             return;
31875         }
31876         if(ce && t == ce.el){
31877             clearTimeout(hideProc);
31878             return;
31879         }
31880         if(t && tagEls[t.id]){
31881             tagEls[t.id].el = t;
31882             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31883             return;
31884         }
31885         var ttp, et = Roo.fly(t);
31886         var ns = cfg.namespace;
31887         if(tm.interceptTitles && t.title){
31888             ttp = t.title;
31889             t.qtip = ttp;
31890             t.removeAttribute("title");
31891             e.preventDefault();
31892         }else{
31893             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31894         }
31895         if(ttp){
31896             showProc = show.defer(tm.showDelay, tm, [{
31897                 el: t, 
31898                 text: ttp, 
31899                 width: et.getAttributeNS(ns, cfg.width),
31900                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31901                 title: et.getAttributeNS(ns, cfg.title),
31902                     cls: et.getAttributeNS(ns, cfg.cls)
31903             }]);
31904         }
31905     };
31906     
31907     var onOut = function(e){
31908         clearTimeout(showProc);
31909         var t = e.getTarget();
31910         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31911             hideProc = setTimeout(hide, tm.hideDelay);
31912         }
31913     };
31914     
31915     var onMove = function(e){
31916         if(disabled){
31917             return;
31918         }
31919         xy = e.getXY();
31920         xy[1] += 18;
31921         if(tm.trackMouse && ce){
31922             el.setXY(xy);
31923         }
31924     };
31925     
31926     var onDown = function(e){
31927         clearTimeout(showProc);
31928         clearTimeout(hideProc);
31929         if(!e.within(el)){
31930             if(tm.hideOnClick){
31931                 hide();
31932                 tm.disable();
31933                 tm.enable.defer(100, tm);
31934             }
31935         }
31936     };
31937     
31938     var getPad = function(){
31939         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31940     };
31941
31942     var show = function(o){
31943         if(disabled){
31944             return;
31945         }
31946         clearTimeout(dismissProc);
31947         ce = o;
31948         if(removeCls){ // in case manually hidden
31949             el.removeClass(removeCls);
31950             removeCls = null;
31951         }
31952         if(ce.cls){
31953             el.addClass(ce.cls);
31954             removeCls = ce.cls;
31955         }
31956         if(ce.title){
31957             tipTitle.update(ce.title);
31958             tipTitle.show();
31959         }else{
31960             tipTitle.update('');
31961             tipTitle.hide();
31962         }
31963         el.dom.style.width  = tm.maxWidth+'px';
31964         //tipBody.dom.style.width = '';
31965         tipBodyText.update(o.text);
31966         var p = getPad(), w = ce.width;
31967         if(!w){
31968             var td = tipBodyText.dom;
31969             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31970             if(aw > tm.maxWidth){
31971                 w = tm.maxWidth;
31972             }else if(aw < tm.minWidth){
31973                 w = tm.minWidth;
31974             }else{
31975                 w = aw;
31976             }
31977         }
31978         //tipBody.setWidth(w);
31979         el.setWidth(parseInt(w, 10) + p);
31980         if(ce.autoHide === false){
31981             close.setDisplayed(true);
31982             if(dd){
31983                 dd.unlock();
31984             }
31985         }else{
31986             close.setDisplayed(false);
31987             if(dd){
31988                 dd.lock();
31989             }
31990         }
31991         if(xy){
31992             el.avoidY = xy[1]-18;
31993             el.setXY(xy);
31994         }
31995         if(tm.animate){
31996             el.setOpacity(.1);
31997             el.setStyle("visibility", "visible");
31998             el.fadeIn({callback: afterShow});
31999         }else{
32000             afterShow();
32001         }
32002     };
32003     
32004     var afterShow = function(){
32005         if(ce){
32006             el.show();
32007             esc.enable();
32008             if(tm.autoDismiss && ce.autoHide !== false){
32009                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32010             }
32011         }
32012     };
32013     
32014     var hide = function(noanim){
32015         clearTimeout(dismissProc);
32016         clearTimeout(hideProc);
32017         ce = null;
32018         if(el.isVisible()){
32019             esc.disable();
32020             if(noanim !== true && tm.animate){
32021                 el.fadeOut({callback: afterHide});
32022             }else{
32023                 afterHide();
32024             } 
32025         }
32026     };
32027     
32028     var afterHide = function(){
32029         el.hide();
32030         if(removeCls){
32031             el.removeClass(removeCls);
32032             removeCls = null;
32033         }
32034     };
32035     
32036     return {
32037         /**
32038         * @cfg {Number} minWidth
32039         * The minimum width of the quick tip (defaults to 40)
32040         */
32041        minWidth : 40,
32042         /**
32043         * @cfg {Number} maxWidth
32044         * The maximum width of the quick tip (defaults to 300)
32045         */
32046        maxWidth : 300,
32047         /**
32048         * @cfg {Boolean} interceptTitles
32049         * True to automatically use the element's DOM title value if available (defaults to false)
32050         */
32051        interceptTitles : false,
32052         /**
32053         * @cfg {Boolean} trackMouse
32054         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32055         */
32056        trackMouse : false,
32057         /**
32058         * @cfg {Boolean} hideOnClick
32059         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32060         */
32061        hideOnClick : true,
32062         /**
32063         * @cfg {Number} showDelay
32064         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32065         */
32066        showDelay : 500,
32067         /**
32068         * @cfg {Number} hideDelay
32069         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32070         */
32071        hideDelay : 200,
32072         /**
32073         * @cfg {Boolean} autoHide
32074         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32075         * Used in conjunction with hideDelay.
32076         */
32077        autoHide : true,
32078         /**
32079         * @cfg {Boolean}
32080         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32081         * (defaults to true).  Used in conjunction with autoDismissDelay.
32082         */
32083        autoDismiss : true,
32084         /**
32085         * @cfg {Number}
32086         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32087         */
32088        autoDismissDelay : 5000,
32089        /**
32090         * @cfg {Boolean} animate
32091         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32092         */
32093        animate : false,
32094
32095        /**
32096         * @cfg {String} title
32097         * Title text to display (defaults to '').  This can be any valid HTML markup.
32098         */
32099         title: '',
32100        /**
32101         * @cfg {String} text
32102         * Body text to display (defaults to '').  This can be any valid HTML markup.
32103         */
32104         text : '',
32105        /**
32106         * @cfg {String} cls
32107         * A CSS class to apply to the base quick tip element (defaults to '').
32108         */
32109         cls : '',
32110        /**
32111         * @cfg {Number} width
32112         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32113         * minWidth or maxWidth.
32114         */
32115         width : null,
32116
32117     /**
32118      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32119      * or display QuickTips in a page.
32120      */
32121        init : function(){
32122           tm = Roo.QuickTips;
32123           cfg = tm.tagConfig;
32124           if(!inited){
32125               if(!Roo.isReady){ // allow calling of init() before onReady
32126                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32127                   return;
32128               }
32129               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32130               el.fxDefaults = {stopFx: true};
32131               // maximum custom styling
32132               //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>');
32133               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>');              
32134               tipTitle = el.child('h3');
32135               tipTitle.enableDisplayMode("block");
32136               tipBody = el.child('div.x-tip-bd');
32137               tipBodyText = el.child('div.x-tip-bd-inner');
32138               //bdLeft = el.child('div.x-tip-bd-left');
32139               //bdRight = el.child('div.x-tip-bd-right');
32140               close = el.child('div.x-tip-close');
32141               close.enableDisplayMode("block");
32142               close.on("click", hide);
32143               var d = Roo.get(document);
32144               d.on("mousedown", onDown);
32145               d.on("mouseover", onOver);
32146               d.on("mouseout", onOut);
32147               d.on("mousemove", onMove);
32148               esc = d.addKeyListener(27, hide);
32149               esc.disable();
32150               if(Roo.dd.DD){
32151                   dd = el.initDD("default", null, {
32152                       onDrag : function(){
32153                           el.sync();  
32154                       }
32155                   });
32156                   dd.setHandleElId(tipTitle.id);
32157                   dd.lock();
32158               }
32159               inited = true;
32160           }
32161           this.enable(); 
32162        },
32163
32164     /**
32165      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32166      * are supported:
32167      * <pre>
32168 Property    Type                   Description
32169 ----------  ---------------------  ------------------------------------------------------------------------
32170 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32171      * </ul>
32172      * @param {Object} config The config object
32173      */
32174        register : function(config){
32175            var cs = config instanceof Array ? config : arguments;
32176            for(var i = 0, len = cs.length; i < len; i++) {
32177                var c = cs[i];
32178                var target = c.target;
32179                if(target){
32180                    if(target instanceof Array){
32181                        for(var j = 0, jlen = target.length; j < jlen; j++){
32182                            tagEls[target[j]] = c;
32183                        }
32184                    }else{
32185                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32186                    }
32187                }
32188            }
32189        },
32190
32191     /**
32192      * Removes this quick tip from its element and destroys it.
32193      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32194      */
32195        unregister : function(el){
32196            delete tagEls[Roo.id(el)];
32197        },
32198
32199     /**
32200      * Enable this quick tip.
32201      */
32202        enable : function(){
32203            if(inited && disabled){
32204                locks.pop();
32205                if(locks.length < 1){
32206                    disabled = false;
32207                }
32208            }
32209        },
32210
32211     /**
32212      * Disable this quick tip.
32213      */
32214        disable : function(){
32215           disabled = true;
32216           clearTimeout(showProc);
32217           clearTimeout(hideProc);
32218           clearTimeout(dismissProc);
32219           if(ce){
32220               hide(true);
32221           }
32222           locks.push(1);
32223        },
32224
32225     /**
32226      * Returns true if the quick tip is enabled, else false.
32227      */
32228        isEnabled : function(){
32229             return !disabled;
32230        },
32231
32232         // private
32233        tagConfig : {
32234            namespace : "ext",
32235            attribute : "qtip",
32236            width : "width",
32237            target : "target",
32238            title : "qtitle",
32239            hide : "hide",
32240            cls : "qclass"
32241        }
32242    };
32243 }();
32244
32245 // backwards compat
32246 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32247  * Based on:
32248  * Ext JS Library 1.1.1
32249  * Copyright(c) 2006-2007, Ext JS, LLC.
32250  *
32251  * Originally Released Under LGPL - original licence link has changed is not relivant.
32252  *
32253  * Fork - LGPL
32254  * <script type="text/javascript">
32255  */
32256  
32257
32258 /**
32259  * @class Roo.tree.TreePanel
32260  * @extends Roo.data.Tree
32261
32262  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32263  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32264  * @cfg {Boolean} enableDD true to enable drag and drop
32265  * @cfg {Boolean} enableDrag true to enable just drag
32266  * @cfg {Boolean} enableDrop true to enable just drop
32267  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32268  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32269  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32270  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32271  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32272  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32273  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32274  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32275  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32276  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32277  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32278  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32279  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32280  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32281  * @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>
32282  * @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>
32283  * 
32284  * @constructor
32285  * @param {String/HTMLElement/Element} el The container element
32286  * @param {Object} config
32287  */
32288 Roo.tree.TreePanel = function(el, config){
32289     var root = false;
32290     var loader = false;
32291     if (config.root) {
32292         root = config.root;
32293         delete config.root;
32294     }
32295     if (config.loader) {
32296         loader = config.loader;
32297         delete config.loader;
32298     }
32299     
32300     Roo.apply(this, config);
32301     Roo.tree.TreePanel.superclass.constructor.call(this);
32302     this.el = Roo.get(el);
32303     this.el.addClass('x-tree');
32304     //console.log(root);
32305     if (root) {
32306         this.setRootNode( Roo.factory(root, Roo.tree));
32307     }
32308     if (loader) {
32309         this.loader = Roo.factory(loader, Roo.tree);
32310     }
32311    /**
32312     * Read-only. The id of the container element becomes this TreePanel's id.
32313     */
32314     this.id = this.el.id;
32315     this.addEvents({
32316         /**
32317         * @event beforeload
32318         * Fires before a node is loaded, return false to cancel
32319         * @param {Node} node The node being loaded
32320         */
32321         "beforeload" : true,
32322         /**
32323         * @event load
32324         * Fires when a node is loaded
32325         * @param {Node} node The node that was loaded
32326         */
32327         "load" : true,
32328         /**
32329         * @event textchange
32330         * Fires when the text for a node is changed
32331         * @param {Node} node The node
32332         * @param {String} text The new text
32333         * @param {String} oldText The old text
32334         */
32335         "textchange" : true,
32336         /**
32337         * @event beforeexpand
32338         * Fires before a node is expanded, return false to cancel.
32339         * @param {Node} node The node
32340         * @param {Boolean} deep
32341         * @param {Boolean} anim
32342         */
32343         "beforeexpand" : true,
32344         /**
32345         * @event beforecollapse
32346         * Fires before a node is collapsed, return false to cancel.
32347         * @param {Node} node The node
32348         * @param {Boolean} deep
32349         * @param {Boolean} anim
32350         */
32351         "beforecollapse" : true,
32352         /**
32353         * @event expand
32354         * Fires when a node is expanded
32355         * @param {Node} node The node
32356         */
32357         "expand" : true,
32358         /**
32359         * @event disabledchange
32360         * Fires when the disabled status of a node changes
32361         * @param {Node} node The node
32362         * @param {Boolean} disabled
32363         */
32364         "disabledchange" : true,
32365         /**
32366         * @event collapse
32367         * Fires when a node is collapsed
32368         * @param {Node} node The node
32369         */
32370         "collapse" : true,
32371         /**
32372         * @event beforeclick
32373         * Fires before click processing on a node. Return false to cancel the default action.
32374         * @param {Node} node The node
32375         * @param {Roo.EventObject} e The event object
32376         */
32377         "beforeclick":true,
32378         /**
32379         * @event checkchange
32380         * Fires when a node with a checkbox's checked property changes
32381         * @param {Node} this This node
32382         * @param {Boolean} checked
32383         */
32384         "checkchange":true,
32385         /**
32386         * @event click
32387         * Fires when a node is clicked
32388         * @param {Node} node The node
32389         * @param {Roo.EventObject} e The event object
32390         */
32391         "click":true,
32392         /**
32393         * @event dblclick
32394         * Fires when a node is double clicked
32395         * @param {Node} node The node
32396         * @param {Roo.EventObject} e The event object
32397         */
32398         "dblclick":true,
32399         /**
32400         * @event contextmenu
32401         * Fires when a node is right clicked
32402         * @param {Node} node The node
32403         * @param {Roo.EventObject} e The event object
32404         */
32405         "contextmenu":true,
32406         /**
32407         * @event beforechildrenrendered
32408         * Fires right before the child nodes for a node are rendered
32409         * @param {Node} node The node
32410         */
32411         "beforechildrenrendered":true,
32412         /**
32413         * @event startdrag
32414         * Fires when a node starts being dragged
32415         * @param {Roo.tree.TreePanel} this
32416         * @param {Roo.tree.TreeNode} node
32417         * @param {event} e The raw browser event
32418         */ 
32419        "startdrag" : true,
32420        /**
32421         * @event enddrag
32422         * Fires when a drag operation is complete
32423         * @param {Roo.tree.TreePanel} this
32424         * @param {Roo.tree.TreeNode} node
32425         * @param {event} e The raw browser event
32426         */
32427        "enddrag" : true,
32428        /**
32429         * @event dragdrop
32430         * Fires when a dragged node is dropped on a valid DD target
32431         * @param {Roo.tree.TreePanel} this
32432         * @param {Roo.tree.TreeNode} node
32433         * @param {DD} dd The dd it was dropped on
32434         * @param {event} e The raw browser event
32435         */
32436        "dragdrop" : true,
32437        /**
32438         * @event beforenodedrop
32439         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32440         * passed to handlers has the following properties:<br />
32441         * <ul style="padding:5px;padding-left:16px;">
32442         * <li>tree - The TreePanel</li>
32443         * <li>target - The node being targeted for the drop</li>
32444         * <li>data - The drag data from the drag source</li>
32445         * <li>point - The point of the drop - append, above or below</li>
32446         * <li>source - The drag source</li>
32447         * <li>rawEvent - Raw mouse event</li>
32448         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32449         * to be inserted by setting them on this object.</li>
32450         * <li>cancel - Set this to true to cancel the drop.</li>
32451         * </ul>
32452         * @param {Object} dropEvent
32453         */
32454        "beforenodedrop" : true,
32455        /**
32456         * @event nodedrop
32457         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32458         * passed to handlers has the following properties:<br />
32459         * <ul style="padding:5px;padding-left:16px;">
32460         * <li>tree - The TreePanel</li>
32461         * <li>target - The node being targeted for the drop</li>
32462         * <li>data - The drag data from the drag source</li>
32463         * <li>point - The point of the drop - append, above or below</li>
32464         * <li>source - The drag source</li>
32465         * <li>rawEvent - Raw mouse event</li>
32466         * <li>dropNode - Dropped node(s).</li>
32467         * </ul>
32468         * @param {Object} dropEvent
32469         */
32470        "nodedrop" : true,
32471         /**
32472         * @event nodedragover
32473         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32474         * passed to handlers has the following properties:<br />
32475         * <ul style="padding:5px;padding-left:16px;">
32476         * <li>tree - The TreePanel</li>
32477         * <li>target - The node being targeted for the drop</li>
32478         * <li>data - The drag data from the drag source</li>
32479         * <li>point - The point of the drop - append, above or below</li>
32480         * <li>source - The drag source</li>
32481         * <li>rawEvent - Raw mouse event</li>
32482         * <li>dropNode - Drop node(s) provided by the source.</li>
32483         * <li>cancel - Set this to true to signal drop not allowed.</li>
32484         * </ul>
32485         * @param {Object} dragOverEvent
32486         */
32487        "nodedragover" : true
32488         
32489     });
32490     if(this.singleExpand){
32491        this.on("beforeexpand", this.restrictExpand, this);
32492     }
32493     if (this.editor) {
32494         this.editor.tree = this;
32495         this.editor = Roo.factory(this.editor, Roo.tree);
32496     }
32497     
32498     if (this.selModel) {
32499         this.selModel = Roo.factory(this.selModel, Roo.tree);
32500     }
32501    
32502 };
32503 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32504     rootVisible : true,
32505     animate: Roo.enableFx,
32506     lines : true,
32507     enableDD : false,
32508     hlDrop : Roo.enableFx,
32509   
32510     renderer: false,
32511     
32512     rendererTip: false,
32513     // private
32514     restrictExpand : function(node){
32515         var p = node.parentNode;
32516         if(p){
32517             if(p.expandedChild && p.expandedChild.parentNode == p){
32518                 p.expandedChild.collapse();
32519             }
32520             p.expandedChild = node;
32521         }
32522     },
32523
32524     // private override
32525     setRootNode : function(node){
32526         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32527         if(!this.rootVisible){
32528             node.ui = new Roo.tree.RootTreeNodeUI(node);
32529         }
32530         return node;
32531     },
32532
32533     /**
32534      * Returns the container element for this TreePanel
32535      */
32536     getEl : function(){
32537         return this.el;
32538     },
32539
32540     /**
32541      * Returns the default TreeLoader for this TreePanel
32542      */
32543     getLoader : function(){
32544         return this.loader;
32545     },
32546
32547     /**
32548      * Expand all nodes
32549      */
32550     expandAll : function(){
32551         this.root.expand(true);
32552     },
32553
32554     /**
32555      * Collapse all nodes
32556      */
32557     collapseAll : function(){
32558         this.root.collapse(true);
32559     },
32560
32561     /**
32562      * Returns the selection model used by this TreePanel
32563      */
32564     getSelectionModel : function(){
32565         if(!this.selModel){
32566             this.selModel = new Roo.tree.DefaultSelectionModel();
32567         }
32568         return this.selModel;
32569     },
32570
32571     /**
32572      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32573      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32574      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32575      * @return {Array}
32576      */
32577     getChecked : function(a, startNode){
32578         startNode = startNode || this.root;
32579         var r = [];
32580         var f = function(){
32581             if(this.attributes.checked){
32582                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32583             }
32584         }
32585         startNode.cascade(f);
32586         return r;
32587     },
32588
32589     /**
32590      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32591      * @param {String} path
32592      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32593      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32594      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32595      */
32596     expandPath : function(path, attr, callback){
32597         attr = attr || "id";
32598         var keys = path.split(this.pathSeparator);
32599         var curNode = this.root;
32600         if(curNode.attributes[attr] != keys[1]){ // invalid root
32601             if(callback){
32602                 callback(false, null);
32603             }
32604             return;
32605         }
32606         var index = 1;
32607         var f = function(){
32608             if(++index == keys.length){
32609                 if(callback){
32610                     callback(true, curNode);
32611                 }
32612                 return;
32613             }
32614             var c = curNode.findChild(attr, keys[index]);
32615             if(!c){
32616                 if(callback){
32617                     callback(false, curNode);
32618                 }
32619                 return;
32620             }
32621             curNode = c;
32622             c.expand(false, false, f);
32623         };
32624         curNode.expand(false, false, f);
32625     },
32626
32627     /**
32628      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32629      * @param {String} path
32630      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32631      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32632      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32633      */
32634     selectPath : function(path, attr, callback){
32635         attr = attr || "id";
32636         var keys = path.split(this.pathSeparator);
32637         var v = keys.pop();
32638         if(keys.length > 0){
32639             var f = function(success, node){
32640                 if(success && node){
32641                     var n = node.findChild(attr, v);
32642                     if(n){
32643                         n.select();
32644                         if(callback){
32645                             callback(true, n);
32646                         }
32647                     }else if(callback){
32648                         callback(false, n);
32649                     }
32650                 }else{
32651                     if(callback){
32652                         callback(false, n);
32653                     }
32654                 }
32655             };
32656             this.expandPath(keys.join(this.pathSeparator), attr, f);
32657         }else{
32658             this.root.select();
32659             if(callback){
32660                 callback(true, this.root);
32661             }
32662         }
32663     },
32664
32665     getTreeEl : function(){
32666         return this.el;
32667     },
32668
32669     /**
32670      * Trigger rendering of this TreePanel
32671      */
32672     render : function(){
32673         if (this.innerCt) {
32674             return this; // stop it rendering more than once!!
32675         }
32676         
32677         this.innerCt = this.el.createChild({tag:"ul",
32678                cls:"x-tree-root-ct " +
32679                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32680
32681         if(this.containerScroll){
32682             Roo.dd.ScrollManager.register(this.el);
32683         }
32684         if((this.enableDD || this.enableDrop) && !this.dropZone){
32685            /**
32686             * The dropZone used by this tree if drop is enabled
32687             * @type Roo.tree.TreeDropZone
32688             */
32689              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32690                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32691            });
32692         }
32693         if((this.enableDD || this.enableDrag) && !this.dragZone){
32694            /**
32695             * The dragZone used by this tree if drag is enabled
32696             * @type Roo.tree.TreeDragZone
32697             */
32698             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32699                ddGroup: this.ddGroup || "TreeDD",
32700                scroll: this.ddScroll
32701            });
32702         }
32703         this.getSelectionModel().init(this);
32704         if (!this.root) {
32705             Roo.log("ROOT not set in tree");
32706             return this;
32707         }
32708         this.root.render();
32709         if(!this.rootVisible){
32710             this.root.renderChildren();
32711         }
32712         return this;
32713     }
32714 });/*
32715  * Based on:
32716  * Ext JS Library 1.1.1
32717  * Copyright(c) 2006-2007, Ext JS, LLC.
32718  *
32719  * Originally Released Under LGPL - original licence link has changed is not relivant.
32720  *
32721  * Fork - LGPL
32722  * <script type="text/javascript">
32723  */
32724  
32725
32726 /**
32727  * @class Roo.tree.DefaultSelectionModel
32728  * @extends Roo.util.Observable
32729  * The default single selection for a TreePanel.
32730  * @param {Object} cfg Configuration
32731  */
32732 Roo.tree.DefaultSelectionModel = function(cfg){
32733    this.selNode = null;
32734    
32735    
32736    
32737    this.addEvents({
32738        /**
32739         * @event selectionchange
32740         * Fires when the selected node changes
32741         * @param {DefaultSelectionModel} this
32742         * @param {TreeNode} node the new selection
32743         */
32744        "selectionchange" : true,
32745
32746        /**
32747         * @event beforeselect
32748         * Fires before the selected node changes, return false to cancel the change
32749         * @param {DefaultSelectionModel} this
32750         * @param {TreeNode} node the new selection
32751         * @param {TreeNode} node the old selection
32752         */
32753        "beforeselect" : true
32754    });
32755    
32756     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32757 };
32758
32759 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32760     init : function(tree){
32761         this.tree = tree;
32762         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32763         tree.on("click", this.onNodeClick, this);
32764     },
32765     
32766     onNodeClick : function(node, e){
32767         if (e.ctrlKey && this.selNode == node)  {
32768             this.unselect(node);
32769             return;
32770         }
32771         this.select(node);
32772     },
32773     
32774     /**
32775      * Select a node.
32776      * @param {TreeNode} node The node to select
32777      * @return {TreeNode} The selected node
32778      */
32779     select : function(node){
32780         var last = this.selNode;
32781         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32782             if(last){
32783                 last.ui.onSelectedChange(false);
32784             }
32785             this.selNode = node;
32786             node.ui.onSelectedChange(true);
32787             this.fireEvent("selectionchange", this, node, last);
32788         }
32789         return node;
32790     },
32791     
32792     /**
32793      * Deselect a node.
32794      * @param {TreeNode} node The node to unselect
32795      */
32796     unselect : function(node){
32797         if(this.selNode == node){
32798             this.clearSelections();
32799         }    
32800     },
32801     
32802     /**
32803      * Clear all selections
32804      */
32805     clearSelections : function(){
32806         var n = this.selNode;
32807         if(n){
32808             n.ui.onSelectedChange(false);
32809             this.selNode = null;
32810             this.fireEvent("selectionchange", this, null);
32811         }
32812         return n;
32813     },
32814     
32815     /**
32816      * Get the selected node
32817      * @return {TreeNode} The selected node
32818      */
32819     getSelectedNode : function(){
32820         return this.selNode;    
32821     },
32822     
32823     /**
32824      * Returns true if the node is selected
32825      * @param {TreeNode} node The node to check
32826      * @return {Boolean}
32827      */
32828     isSelected : function(node){
32829         return this.selNode == node;  
32830     },
32831
32832     /**
32833      * Selects the node above the selected node in the tree, intelligently walking the nodes
32834      * @return TreeNode The new selection
32835      */
32836     selectPrevious : function(){
32837         var s = this.selNode || this.lastSelNode;
32838         if(!s){
32839             return null;
32840         }
32841         var ps = s.previousSibling;
32842         if(ps){
32843             if(!ps.isExpanded() || ps.childNodes.length < 1){
32844                 return this.select(ps);
32845             } else{
32846                 var lc = ps.lastChild;
32847                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32848                     lc = lc.lastChild;
32849                 }
32850                 return this.select(lc);
32851             }
32852         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32853             return this.select(s.parentNode);
32854         }
32855         return null;
32856     },
32857
32858     /**
32859      * Selects the node above the selected node in the tree, intelligently walking the nodes
32860      * @return TreeNode The new selection
32861      */
32862     selectNext : function(){
32863         var s = this.selNode || this.lastSelNode;
32864         if(!s){
32865             return null;
32866         }
32867         if(s.firstChild && s.isExpanded()){
32868              return this.select(s.firstChild);
32869          }else if(s.nextSibling){
32870              return this.select(s.nextSibling);
32871          }else if(s.parentNode){
32872             var newS = null;
32873             s.parentNode.bubble(function(){
32874                 if(this.nextSibling){
32875                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32876                     return false;
32877                 }
32878             });
32879             return newS;
32880          }
32881         return null;
32882     },
32883
32884     onKeyDown : function(e){
32885         var s = this.selNode || this.lastSelNode;
32886         // undesirable, but required
32887         var sm = this;
32888         if(!s){
32889             return;
32890         }
32891         var k = e.getKey();
32892         switch(k){
32893              case e.DOWN:
32894                  e.stopEvent();
32895                  this.selectNext();
32896              break;
32897              case e.UP:
32898                  e.stopEvent();
32899                  this.selectPrevious();
32900              break;
32901              case e.RIGHT:
32902                  e.preventDefault();
32903                  if(s.hasChildNodes()){
32904                      if(!s.isExpanded()){
32905                          s.expand();
32906                      }else if(s.firstChild){
32907                          this.select(s.firstChild, e);
32908                      }
32909                  }
32910              break;
32911              case e.LEFT:
32912                  e.preventDefault();
32913                  if(s.hasChildNodes() && s.isExpanded()){
32914                      s.collapse();
32915                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32916                      this.select(s.parentNode, e);
32917                  }
32918              break;
32919         };
32920     }
32921 });
32922
32923 /**
32924  * @class Roo.tree.MultiSelectionModel
32925  * @extends Roo.util.Observable
32926  * Multi selection for a TreePanel.
32927  * @param {Object} cfg Configuration
32928  */
32929 Roo.tree.MultiSelectionModel = function(){
32930    this.selNodes = [];
32931    this.selMap = {};
32932    this.addEvents({
32933        /**
32934         * @event selectionchange
32935         * Fires when the selected nodes change
32936         * @param {MultiSelectionModel} this
32937         * @param {Array} nodes Array of the selected nodes
32938         */
32939        "selectionchange" : true
32940    });
32941    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32942    
32943 };
32944
32945 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32946     init : function(tree){
32947         this.tree = tree;
32948         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32949         tree.on("click", this.onNodeClick, this);
32950     },
32951     
32952     onNodeClick : function(node, e){
32953         this.select(node, e, e.ctrlKey);
32954     },
32955     
32956     /**
32957      * Select a node.
32958      * @param {TreeNode} node The node to select
32959      * @param {EventObject} e (optional) An event associated with the selection
32960      * @param {Boolean} keepExisting True to retain existing selections
32961      * @return {TreeNode} The selected node
32962      */
32963     select : function(node, e, keepExisting){
32964         if(keepExisting !== true){
32965             this.clearSelections(true);
32966         }
32967         if(this.isSelected(node)){
32968             this.lastSelNode = node;
32969             return node;
32970         }
32971         this.selNodes.push(node);
32972         this.selMap[node.id] = node;
32973         this.lastSelNode = node;
32974         node.ui.onSelectedChange(true);
32975         this.fireEvent("selectionchange", this, this.selNodes);
32976         return node;
32977     },
32978     
32979     /**
32980      * Deselect a node.
32981      * @param {TreeNode} node The node to unselect
32982      */
32983     unselect : function(node){
32984         if(this.selMap[node.id]){
32985             node.ui.onSelectedChange(false);
32986             var sn = this.selNodes;
32987             var index = -1;
32988             if(sn.indexOf){
32989                 index = sn.indexOf(node);
32990             }else{
32991                 for(var i = 0, len = sn.length; i < len; i++){
32992                     if(sn[i] == node){
32993                         index = i;
32994                         break;
32995                     }
32996                 }
32997             }
32998             if(index != -1){
32999                 this.selNodes.splice(index, 1);
33000             }
33001             delete this.selMap[node.id];
33002             this.fireEvent("selectionchange", this, this.selNodes);
33003         }
33004     },
33005     
33006     /**
33007      * Clear all selections
33008      */
33009     clearSelections : function(suppressEvent){
33010         var sn = this.selNodes;
33011         if(sn.length > 0){
33012             for(var i = 0, len = sn.length; i < len; i++){
33013                 sn[i].ui.onSelectedChange(false);
33014             }
33015             this.selNodes = [];
33016             this.selMap = {};
33017             if(suppressEvent !== true){
33018                 this.fireEvent("selectionchange", this, this.selNodes);
33019             }
33020         }
33021     },
33022     
33023     /**
33024      * Returns true if the node is selected
33025      * @param {TreeNode} node The node to check
33026      * @return {Boolean}
33027      */
33028     isSelected : function(node){
33029         return this.selMap[node.id] ? true : false;  
33030     },
33031     
33032     /**
33033      * Returns an array of the selected nodes
33034      * @return {Array}
33035      */
33036     getSelectedNodes : function(){
33037         return this.selNodes;    
33038     },
33039
33040     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33041
33042     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33043
33044     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33045 });/*
33046  * Based on:
33047  * Ext JS Library 1.1.1
33048  * Copyright(c) 2006-2007, Ext JS, LLC.
33049  *
33050  * Originally Released Under LGPL - original licence link has changed is not relivant.
33051  *
33052  * Fork - LGPL
33053  * <script type="text/javascript">
33054  */
33055  
33056 /**
33057  * @class Roo.tree.TreeNode
33058  * @extends Roo.data.Node
33059  * @cfg {String} text The text for this node
33060  * @cfg {Boolean} expanded true to start the node expanded
33061  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33062  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33063  * @cfg {Boolean} disabled true to start the node disabled
33064  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33065  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33066  * @cfg {String} cls A css class to be added to the node
33067  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33068  * @cfg {String} href URL of the link used for the node (defaults to #)
33069  * @cfg {String} hrefTarget target frame for the link
33070  * @cfg {String} qtip An Ext QuickTip for the node
33071  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33072  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33073  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33074  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33075  * (defaults to undefined with no checkbox rendered)
33076  * @constructor
33077  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33078  */
33079 Roo.tree.TreeNode = function(attributes){
33080     attributes = attributes || {};
33081     if(typeof attributes == "string"){
33082         attributes = {text: attributes};
33083     }
33084     this.childrenRendered = false;
33085     this.rendered = false;
33086     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33087     this.expanded = attributes.expanded === true;
33088     this.isTarget = attributes.isTarget !== false;
33089     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33090     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33091
33092     /**
33093      * Read-only. The text for this node. To change it use setText().
33094      * @type String
33095      */
33096     this.text = attributes.text;
33097     /**
33098      * True if this node is disabled.
33099      * @type Boolean
33100      */
33101     this.disabled = attributes.disabled === true;
33102
33103     this.addEvents({
33104         /**
33105         * @event textchange
33106         * Fires when the text for this node is changed
33107         * @param {Node} this This node
33108         * @param {String} text The new text
33109         * @param {String} oldText The old text
33110         */
33111         "textchange" : true,
33112         /**
33113         * @event beforeexpand
33114         * Fires before this node is expanded, return false to cancel.
33115         * @param {Node} this This node
33116         * @param {Boolean} deep
33117         * @param {Boolean} anim
33118         */
33119         "beforeexpand" : true,
33120         /**
33121         * @event beforecollapse
33122         * Fires before this node is collapsed, return false to cancel.
33123         * @param {Node} this This node
33124         * @param {Boolean} deep
33125         * @param {Boolean} anim
33126         */
33127         "beforecollapse" : true,
33128         /**
33129         * @event expand
33130         * Fires when this node is expanded
33131         * @param {Node} this This node
33132         */
33133         "expand" : true,
33134         /**
33135         * @event disabledchange
33136         * Fires when the disabled status of this node changes
33137         * @param {Node} this This node
33138         * @param {Boolean} disabled
33139         */
33140         "disabledchange" : true,
33141         /**
33142         * @event collapse
33143         * Fires when this node is collapsed
33144         * @param {Node} this This node
33145         */
33146         "collapse" : true,
33147         /**
33148         * @event beforeclick
33149         * Fires before click processing. Return false to cancel the default action.
33150         * @param {Node} this This node
33151         * @param {Roo.EventObject} e The event object
33152         */
33153         "beforeclick":true,
33154         /**
33155         * @event checkchange
33156         * Fires when a node with a checkbox's checked property changes
33157         * @param {Node} this This node
33158         * @param {Boolean} checked
33159         */
33160         "checkchange":true,
33161         /**
33162         * @event click
33163         * Fires when this node is clicked
33164         * @param {Node} this This node
33165         * @param {Roo.EventObject} e The event object
33166         */
33167         "click":true,
33168         /**
33169         * @event dblclick
33170         * Fires when this node is double clicked
33171         * @param {Node} this This node
33172         * @param {Roo.EventObject} e The event object
33173         */
33174         "dblclick":true,
33175         /**
33176         * @event contextmenu
33177         * Fires when this node is right clicked
33178         * @param {Node} this This node
33179         * @param {Roo.EventObject} e The event object
33180         */
33181         "contextmenu":true,
33182         /**
33183         * @event beforechildrenrendered
33184         * Fires right before the child nodes for this node are rendered
33185         * @param {Node} this This node
33186         */
33187         "beforechildrenrendered":true
33188     });
33189
33190     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33191
33192     /**
33193      * Read-only. The UI for this node
33194      * @type TreeNodeUI
33195      */
33196     this.ui = new uiClass(this);
33197     
33198     // finally support items[]
33199     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33200         return;
33201     }
33202     
33203     
33204     Roo.each(this.attributes.items, function(c) {
33205         this.appendChild(Roo.factory(c,Roo.Tree));
33206     }, this);
33207     delete this.attributes.items;
33208     
33209     
33210     
33211 };
33212 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33213     preventHScroll: true,
33214     /**
33215      * Returns true if this node is expanded
33216      * @return {Boolean}
33217      */
33218     isExpanded : function(){
33219         return this.expanded;
33220     },
33221
33222     /**
33223      * Returns the UI object for this node
33224      * @return {TreeNodeUI}
33225      */
33226     getUI : function(){
33227         return this.ui;
33228     },
33229
33230     // private override
33231     setFirstChild : function(node){
33232         var of = this.firstChild;
33233         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33234         if(this.childrenRendered && of && node != of){
33235             of.renderIndent(true, true);
33236         }
33237         if(this.rendered){
33238             this.renderIndent(true, true);
33239         }
33240     },
33241
33242     // private override
33243     setLastChild : function(node){
33244         var ol = this.lastChild;
33245         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33246         if(this.childrenRendered && ol && node != ol){
33247             ol.renderIndent(true, true);
33248         }
33249         if(this.rendered){
33250             this.renderIndent(true, true);
33251         }
33252     },
33253
33254     // these methods are overridden to provide lazy rendering support
33255     // private override
33256     appendChild : function()
33257     {
33258         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33259         if(node && this.childrenRendered){
33260             node.render();
33261         }
33262         this.ui.updateExpandIcon();
33263         return node;
33264     },
33265
33266     // private override
33267     removeChild : function(node){
33268         this.ownerTree.getSelectionModel().unselect(node);
33269         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33270         // if it's been rendered remove dom node
33271         if(this.childrenRendered){
33272             node.ui.remove();
33273         }
33274         if(this.childNodes.length < 1){
33275             this.collapse(false, false);
33276         }else{
33277             this.ui.updateExpandIcon();
33278         }
33279         if(!this.firstChild) {
33280             this.childrenRendered = false;
33281         }
33282         return node;
33283     },
33284
33285     // private override
33286     insertBefore : function(node, refNode){
33287         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33288         if(newNode && refNode && this.childrenRendered){
33289             node.render();
33290         }
33291         this.ui.updateExpandIcon();
33292         return newNode;
33293     },
33294
33295     /**
33296      * Sets the text for this node
33297      * @param {String} text
33298      */
33299     setText : function(text){
33300         var oldText = this.text;
33301         this.text = text;
33302         this.attributes.text = text;
33303         if(this.rendered){ // event without subscribing
33304             this.ui.onTextChange(this, text, oldText);
33305         }
33306         this.fireEvent("textchange", this, text, oldText);
33307     },
33308
33309     /**
33310      * Triggers selection of this node
33311      */
33312     select : function(){
33313         this.getOwnerTree().getSelectionModel().select(this);
33314     },
33315
33316     /**
33317      * Triggers deselection of this node
33318      */
33319     unselect : function(){
33320         this.getOwnerTree().getSelectionModel().unselect(this);
33321     },
33322
33323     /**
33324      * Returns true if this node is selected
33325      * @return {Boolean}
33326      */
33327     isSelected : function(){
33328         return this.getOwnerTree().getSelectionModel().isSelected(this);
33329     },
33330
33331     /**
33332      * Expand this node.
33333      * @param {Boolean} deep (optional) True to expand all children as well
33334      * @param {Boolean} anim (optional) false to cancel the default animation
33335      * @param {Function} callback (optional) A callback to be called when
33336      * expanding this node completes (does not wait for deep expand to complete).
33337      * Called with 1 parameter, this node.
33338      */
33339     expand : function(deep, anim, callback){
33340         if(!this.expanded){
33341             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33342                 return;
33343             }
33344             if(!this.childrenRendered){
33345                 this.renderChildren();
33346             }
33347             this.expanded = true;
33348             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33349                 this.ui.animExpand(function(){
33350                     this.fireEvent("expand", this);
33351                     if(typeof callback == "function"){
33352                         callback(this);
33353                     }
33354                     if(deep === true){
33355                         this.expandChildNodes(true);
33356                     }
33357                 }.createDelegate(this));
33358                 return;
33359             }else{
33360                 this.ui.expand();
33361                 this.fireEvent("expand", this);
33362                 if(typeof callback == "function"){
33363                     callback(this);
33364                 }
33365             }
33366         }else{
33367            if(typeof callback == "function"){
33368                callback(this);
33369            }
33370         }
33371         if(deep === true){
33372             this.expandChildNodes(true);
33373         }
33374     },
33375
33376     isHiddenRoot : function(){
33377         return this.isRoot && !this.getOwnerTree().rootVisible;
33378     },
33379
33380     /**
33381      * Collapse this node.
33382      * @param {Boolean} deep (optional) True to collapse all children as well
33383      * @param {Boolean} anim (optional) false to cancel the default animation
33384      */
33385     collapse : function(deep, anim){
33386         if(this.expanded && !this.isHiddenRoot()){
33387             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33388                 return;
33389             }
33390             this.expanded = false;
33391             if((this.getOwnerTree().animate && anim !== false) || anim){
33392                 this.ui.animCollapse(function(){
33393                     this.fireEvent("collapse", this);
33394                     if(deep === true){
33395                         this.collapseChildNodes(true);
33396                     }
33397                 }.createDelegate(this));
33398                 return;
33399             }else{
33400                 this.ui.collapse();
33401                 this.fireEvent("collapse", this);
33402             }
33403         }
33404         if(deep === true){
33405             var cs = this.childNodes;
33406             for(var i = 0, len = cs.length; i < len; i++) {
33407                 cs[i].collapse(true, false);
33408             }
33409         }
33410     },
33411
33412     // private
33413     delayedExpand : function(delay){
33414         if(!this.expandProcId){
33415             this.expandProcId = this.expand.defer(delay, this);
33416         }
33417     },
33418
33419     // private
33420     cancelExpand : function(){
33421         if(this.expandProcId){
33422             clearTimeout(this.expandProcId);
33423         }
33424         this.expandProcId = false;
33425     },
33426
33427     /**
33428      * Toggles expanded/collapsed state of the node
33429      */
33430     toggle : function(){
33431         if(this.expanded){
33432             this.collapse();
33433         }else{
33434             this.expand();
33435         }
33436     },
33437
33438     /**
33439      * Ensures all parent nodes are expanded
33440      */
33441     ensureVisible : function(callback){
33442         var tree = this.getOwnerTree();
33443         tree.expandPath(this.parentNode.getPath(), false, function(){
33444             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33445             Roo.callback(callback);
33446         }.createDelegate(this));
33447     },
33448
33449     /**
33450      * Expand all child nodes
33451      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33452      */
33453     expandChildNodes : function(deep){
33454         var cs = this.childNodes;
33455         for(var i = 0, len = cs.length; i < len; i++) {
33456                 cs[i].expand(deep);
33457         }
33458     },
33459
33460     /**
33461      * Collapse all child nodes
33462      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33463      */
33464     collapseChildNodes : function(deep){
33465         var cs = this.childNodes;
33466         for(var i = 0, len = cs.length; i < len; i++) {
33467                 cs[i].collapse(deep);
33468         }
33469     },
33470
33471     /**
33472      * Disables this node
33473      */
33474     disable : function(){
33475         this.disabled = true;
33476         this.unselect();
33477         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33478             this.ui.onDisableChange(this, true);
33479         }
33480         this.fireEvent("disabledchange", this, true);
33481     },
33482
33483     /**
33484      * Enables this node
33485      */
33486     enable : function(){
33487         this.disabled = false;
33488         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33489             this.ui.onDisableChange(this, false);
33490         }
33491         this.fireEvent("disabledchange", this, false);
33492     },
33493
33494     // private
33495     renderChildren : function(suppressEvent){
33496         if(suppressEvent !== false){
33497             this.fireEvent("beforechildrenrendered", this);
33498         }
33499         var cs = this.childNodes;
33500         for(var i = 0, len = cs.length; i < len; i++){
33501             cs[i].render(true);
33502         }
33503         this.childrenRendered = true;
33504     },
33505
33506     // private
33507     sort : function(fn, scope){
33508         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33509         if(this.childrenRendered){
33510             var cs = this.childNodes;
33511             for(var i = 0, len = cs.length; i < len; i++){
33512                 cs[i].render(true);
33513             }
33514         }
33515     },
33516
33517     // private
33518     render : function(bulkRender){
33519         this.ui.render(bulkRender);
33520         if(!this.rendered){
33521             this.rendered = true;
33522             if(this.expanded){
33523                 this.expanded = false;
33524                 this.expand(false, false);
33525             }
33526         }
33527     },
33528
33529     // private
33530     renderIndent : function(deep, refresh){
33531         if(refresh){
33532             this.ui.childIndent = null;
33533         }
33534         this.ui.renderIndent();
33535         if(deep === true && this.childrenRendered){
33536             var cs = this.childNodes;
33537             for(var i = 0, len = cs.length; i < len; i++){
33538                 cs[i].renderIndent(true, refresh);
33539             }
33540         }
33541     }
33542 });/*
33543  * Based on:
33544  * Ext JS Library 1.1.1
33545  * Copyright(c) 2006-2007, Ext JS, LLC.
33546  *
33547  * Originally Released Under LGPL - original licence link has changed is not relivant.
33548  *
33549  * Fork - LGPL
33550  * <script type="text/javascript">
33551  */
33552  
33553 /**
33554  * @class Roo.tree.AsyncTreeNode
33555  * @extends Roo.tree.TreeNode
33556  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33557  * @constructor
33558  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33559  */
33560  Roo.tree.AsyncTreeNode = function(config){
33561     this.loaded = false;
33562     this.loading = false;
33563     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33564     /**
33565     * @event beforeload
33566     * Fires before this node is loaded, return false to cancel
33567     * @param {Node} this This node
33568     */
33569     this.addEvents({'beforeload':true, 'load': true});
33570     /**
33571     * @event load
33572     * Fires when this node is loaded
33573     * @param {Node} this This node
33574     */
33575     /**
33576      * The loader used by this node (defaults to using the tree's defined loader)
33577      * @type TreeLoader
33578      * @property loader
33579      */
33580 };
33581 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33582     expand : function(deep, anim, callback){
33583         if(this.loading){ // if an async load is already running, waiting til it's done
33584             var timer;
33585             var f = function(){
33586                 if(!this.loading){ // done loading
33587                     clearInterval(timer);
33588                     this.expand(deep, anim, callback);
33589                 }
33590             }.createDelegate(this);
33591             timer = setInterval(f, 200);
33592             return;
33593         }
33594         if(!this.loaded){
33595             if(this.fireEvent("beforeload", this) === false){
33596                 return;
33597             }
33598             this.loading = true;
33599             this.ui.beforeLoad(this);
33600             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33601             if(loader){
33602                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33603                 return;
33604             }
33605         }
33606         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33607     },
33608     
33609     /**
33610      * Returns true if this node is currently loading
33611      * @return {Boolean}
33612      */
33613     isLoading : function(){
33614         return this.loading;  
33615     },
33616     
33617     loadComplete : function(deep, anim, callback){
33618         this.loading = false;
33619         this.loaded = true;
33620         this.ui.afterLoad(this);
33621         this.fireEvent("load", this);
33622         this.expand(deep, anim, callback);
33623     },
33624     
33625     /**
33626      * Returns true if this node has been loaded
33627      * @return {Boolean}
33628      */
33629     isLoaded : function(){
33630         return this.loaded;
33631     },
33632     
33633     hasChildNodes : function(){
33634         if(!this.isLeaf() && !this.loaded){
33635             return true;
33636         }else{
33637             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33638         }
33639     },
33640
33641     /**
33642      * Trigger a reload for this node
33643      * @param {Function} callback
33644      */
33645     reload : function(callback){
33646         this.collapse(false, false);
33647         while(this.firstChild){
33648             this.removeChild(this.firstChild);
33649         }
33650         this.childrenRendered = false;
33651         this.loaded = false;
33652         if(this.isHiddenRoot()){
33653             this.expanded = false;
33654         }
33655         this.expand(false, false, callback);
33656     }
33657 });/*
33658  * Based on:
33659  * Ext JS Library 1.1.1
33660  * Copyright(c) 2006-2007, Ext JS, LLC.
33661  *
33662  * Originally Released Under LGPL - original licence link has changed is not relivant.
33663  *
33664  * Fork - LGPL
33665  * <script type="text/javascript">
33666  */
33667  
33668 /**
33669  * @class Roo.tree.TreeNodeUI
33670  * @constructor
33671  * @param {Object} node The node to render
33672  * The TreeNode UI implementation is separate from the
33673  * tree implementation. Unless you are customizing the tree UI,
33674  * you should never have to use this directly.
33675  */
33676 Roo.tree.TreeNodeUI = function(node){
33677     this.node = node;
33678     this.rendered = false;
33679     this.animating = false;
33680     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33681 };
33682
33683 Roo.tree.TreeNodeUI.prototype = {
33684     removeChild : function(node){
33685         if(this.rendered){
33686             this.ctNode.removeChild(node.ui.getEl());
33687         }
33688     },
33689
33690     beforeLoad : function(){
33691          this.addClass("x-tree-node-loading");
33692     },
33693
33694     afterLoad : function(){
33695          this.removeClass("x-tree-node-loading");
33696     },
33697
33698     onTextChange : function(node, text, oldText){
33699         if(this.rendered){
33700             this.textNode.innerHTML = text;
33701         }
33702     },
33703
33704     onDisableChange : function(node, state){
33705         this.disabled = state;
33706         if(state){
33707             this.addClass("x-tree-node-disabled");
33708         }else{
33709             this.removeClass("x-tree-node-disabled");
33710         }
33711     },
33712
33713     onSelectedChange : function(state){
33714         if(state){
33715             this.focus();
33716             this.addClass("x-tree-selected");
33717         }else{
33718             //this.blur();
33719             this.removeClass("x-tree-selected");
33720         }
33721     },
33722
33723     onMove : function(tree, node, oldParent, newParent, index, refNode){
33724         this.childIndent = null;
33725         if(this.rendered){
33726             var targetNode = newParent.ui.getContainer();
33727             if(!targetNode){//target not rendered
33728                 this.holder = document.createElement("div");
33729                 this.holder.appendChild(this.wrap);
33730                 return;
33731             }
33732             var insertBefore = refNode ? refNode.ui.getEl() : null;
33733             if(insertBefore){
33734                 targetNode.insertBefore(this.wrap, insertBefore);
33735             }else{
33736                 targetNode.appendChild(this.wrap);
33737             }
33738             this.node.renderIndent(true);
33739         }
33740     },
33741
33742     addClass : function(cls){
33743         if(this.elNode){
33744             Roo.fly(this.elNode).addClass(cls);
33745         }
33746     },
33747
33748     removeClass : function(cls){
33749         if(this.elNode){
33750             Roo.fly(this.elNode).removeClass(cls);
33751         }
33752     },
33753
33754     remove : function(){
33755         if(this.rendered){
33756             this.holder = document.createElement("div");
33757             this.holder.appendChild(this.wrap);
33758         }
33759     },
33760
33761     fireEvent : function(){
33762         return this.node.fireEvent.apply(this.node, arguments);
33763     },
33764
33765     initEvents : function(){
33766         this.node.on("move", this.onMove, this);
33767         var E = Roo.EventManager;
33768         var a = this.anchor;
33769
33770         var el = Roo.fly(a, '_treeui');
33771
33772         if(Roo.isOpera){ // opera render bug ignores the CSS
33773             el.setStyle("text-decoration", "none");
33774         }
33775
33776         el.on("click", this.onClick, this);
33777         el.on("dblclick", this.onDblClick, this);
33778
33779         if(this.checkbox){
33780             Roo.EventManager.on(this.checkbox,
33781                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33782         }
33783
33784         el.on("contextmenu", this.onContextMenu, this);
33785
33786         var icon = Roo.fly(this.iconNode);
33787         icon.on("click", this.onClick, this);
33788         icon.on("dblclick", this.onDblClick, this);
33789         icon.on("contextmenu", this.onContextMenu, this);
33790         E.on(this.ecNode, "click", this.ecClick, this, true);
33791
33792         if(this.node.disabled){
33793             this.addClass("x-tree-node-disabled");
33794         }
33795         if(this.node.hidden){
33796             this.addClass("x-tree-node-disabled");
33797         }
33798         var ot = this.node.getOwnerTree();
33799         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33800         if(dd && (!this.node.isRoot || ot.rootVisible)){
33801             Roo.dd.Registry.register(this.elNode, {
33802                 node: this.node,
33803                 handles: this.getDDHandles(),
33804                 isHandle: false
33805             });
33806         }
33807     },
33808
33809     getDDHandles : function(){
33810         return [this.iconNode, this.textNode];
33811     },
33812
33813     hide : function(){
33814         if(this.rendered){
33815             this.wrap.style.display = "none";
33816         }
33817     },
33818
33819     show : function(){
33820         if(this.rendered){
33821             this.wrap.style.display = "";
33822         }
33823     },
33824
33825     onContextMenu : function(e){
33826         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33827             e.preventDefault();
33828             this.focus();
33829             this.fireEvent("contextmenu", this.node, e);
33830         }
33831     },
33832
33833     onClick : function(e){
33834         if(this.dropping){
33835             e.stopEvent();
33836             return;
33837         }
33838         if(this.fireEvent("beforeclick", this.node, e) !== false){
33839             if(!this.disabled && this.node.attributes.href){
33840                 this.fireEvent("click", this.node, e);
33841                 return;
33842             }
33843             e.preventDefault();
33844             if(this.disabled){
33845                 return;
33846             }
33847
33848             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33849                 this.node.toggle();
33850             }
33851
33852             this.fireEvent("click", this.node, e);
33853         }else{
33854             e.stopEvent();
33855         }
33856     },
33857
33858     onDblClick : function(e){
33859         e.preventDefault();
33860         if(this.disabled){
33861             return;
33862         }
33863         if(this.checkbox){
33864             this.toggleCheck();
33865         }
33866         if(!this.animating && this.node.hasChildNodes()){
33867             this.node.toggle();
33868         }
33869         this.fireEvent("dblclick", this.node, e);
33870     },
33871
33872     onCheckChange : function(){
33873         var checked = this.checkbox.checked;
33874         this.node.attributes.checked = checked;
33875         this.fireEvent('checkchange', this.node, checked);
33876     },
33877
33878     ecClick : function(e){
33879         if(!this.animating && this.node.hasChildNodes()){
33880             this.node.toggle();
33881         }
33882     },
33883
33884     startDrop : function(){
33885         this.dropping = true;
33886     },
33887
33888     // delayed drop so the click event doesn't get fired on a drop
33889     endDrop : function(){
33890        setTimeout(function(){
33891            this.dropping = false;
33892        }.createDelegate(this), 50);
33893     },
33894
33895     expand : function(){
33896         this.updateExpandIcon();
33897         this.ctNode.style.display = "";
33898     },
33899
33900     focus : function(){
33901         if(!this.node.preventHScroll){
33902             try{this.anchor.focus();
33903             }catch(e){}
33904         }else if(!Roo.isIE){
33905             try{
33906                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33907                 var l = noscroll.scrollLeft;
33908                 this.anchor.focus();
33909                 noscroll.scrollLeft = l;
33910             }catch(e){}
33911         }
33912     },
33913
33914     toggleCheck : function(value){
33915         var cb = this.checkbox;
33916         if(cb){
33917             cb.checked = (value === undefined ? !cb.checked : value);
33918         }
33919     },
33920
33921     blur : function(){
33922         try{
33923             this.anchor.blur();
33924         }catch(e){}
33925     },
33926
33927     animExpand : function(callback){
33928         var ct = Roo.get(this.ctNode);
33929         ct.stopFx();
33930         if(!this.node.hasChildNodes()){
33931             this.updateExpandIcon();
33932             this.ctNode.style.display = "";
33933             Roo.callback(callback);
33934             return;
33935         }
33936         this.animating = true;
33937         this.updateExpandIcon();
33938
33939         ct.slideIn('t', {
33940            callback : function(){
33941                this.animating = false;
33942                Roo.callback(callback);
33943             },
33944             scope: this,
33945             duration: this.node.ownerTree.duration || .25
33946         });
33947     },
33948
33949     highlight : function(){
33950         var tree = this.node.getOwnerTree();
33951         Roo.fly(this.wrap).highlight(
33952             tree.hlColor || "C3DAF9",
33953             {endColor: tree.hlBaseColor}
33954         );
33955     },
33956
33957     collapse : function(){
33958         this.updateExpandIcon();
33959         this.ctNode.style.display = "none";
33960     },
33961
33962     animCollapse : function(callback){
33963         var ct = Roo.get(this.ctNode);
33964         ct.enableDisplayMode('block');
33965         ct.stopFx();
33966
33967         this.animating = true;
33968         this.updateExpandIcon();
33969
33970         ct.slideOut('t', {
33971             callback : function(){
33972                this.animating = false;
33973                Roo.callback(callback);
33974             },
33975             scope: this,
33976             duration: this.node.ownerTree.duration || .25
33977         });
33978     },
33979
33980     getContainer : function(){
33981         return this.ctNode;
33982     },
33983
33984     getEl : function(){
33985         return this.wrap;
33986     },
33987
33988     appendDDGhost : function(ghostNode){
33989         ghostNode.appendChild(this.elNode.cloneNode(true));
33990     },
33991
33992     getDDRepairXY : function(){
33993         return Roo.lib.Dom.getXY(this.iconNode);
33994     },
33995
33996     onRender : function(){
33997         this.render();
33998     },
33999
34000     render : function(bulkRender){
34001         var n = this.node, a = n.attributes;
34002         var targetNode = n.parentNode ?
34003               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34004
34005         if(!this.rendered){
34006             this.rendered = true;
34007
34008             this.renderElements(n, a, targetNode, bulkRender);
34009
34010             if(a.qtip){
34011                if(this.textNode.setAttributeNS){
34012                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34013                    if(a.qtipTitle){
34014                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34015                    }
34016                }else{
34017                    this.textNode.setAttribute("ext:qtip", a.qtip);
34018                    if(a.qtipTitle){
34019                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34020                    }
34021                }
34022             }else if(a.qtipCfg){
34023                 a.qtipCfg.target = Roo.id(this.textNode);
34024                 Roo.QuickTips.register(a.qtipCfg);
34025             }
34026             this.initEvents();
34027             if(!this.node.expanded){
34028                 this.updateExpandIcon();
34029             }
34030         }else{
34031             if(bulkRender === true) {
34032                 targetNode.appendChild(this.wrap);
34033             }
34034         }
34035     },
34036
34037     renderElements : function(n, a, targetNode, bulkRender)
34038     {
34039         // add some indent caching, this helps performance when rendering a large tree
34040         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34041         var t = n.getOwnerTree();
34042         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34043         if (typeof(n.attributes.html) != 'undefined') {
34044             txt = n.attributes.html;
34045         }
34046         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34047         var cb = typeof a.checked == 'boolean';
34048         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34049         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34050             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34051             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34052             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34053             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34054             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34055              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34056                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34057             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34058             "</li>"];
34059
34060         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34061             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34062                                 n.nextSibling.ui.getEl(), buf.join(""));
34063         }else{
34064             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34065         }
34066
34067         this.elNode = this.wrap.childNodes[0];
34068         this.ctNode = this.wrap.childNodes[1];
34069         var cs = this.elNode.childNodes;
34070         this.indentNode = cs[0];
34071         this.ecNode = cs[1];
34072         this.iconNode = cs[2];
34073         var index = 3;
34074         if(cb){
34075             this.checkbox = cs[3];
34076             index++;
34077         }
34078         this.anchor = cs[index];
34079         this.textNode = cs[index].firstChild;
34080     },
34081
34082     getAnchor : function(){
34083         return this.anchor;
34084     },
34085
34086     getTextEl : function(){
34087         return this.textNode;
34088     },
34089
34090     getIconEl : function(){
34091         return this.iconNode;
34092     },
34093
34094     isChecked : function(){
34095         return this.checkbox ? this.checkbox.checked : false;
34096     },
34097
34098     updateExpandIcon : function(){
34099         if(this.rendered){
34100             var n = this.node, c1, c2;
34101             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34102             var hasChild = n.hasChildNodes();
34103             if(hasChild){
34104                 if(n.expanded){
34105                     cls += "-minus";
34106                     c1 = "x-tree-node-collapsed";
34107                     c2 = "x-tree-node-expanded";
34108                 }else{
34109                     cls += "-plus";
34110                     c1 = "x-tree-node-expanded";
34111                     c2 = "x-tree-node-collapsed";
34112                 }
34113                 if(this.wasLeaf){
34114                     this.removeClass("x-tree-node-leaf");
34115                     this.wasLeaf = false;
34116                 }
34117                 if(this.c1 != c1 || this.c2 != c2){
34118                     Roo.fly(this.elNode).replaceClass(c1, c2);
34119                     this.c1 = c1; this.c2 = c2;
34120                 }
34121             }else{
34122                 // this changes non-leafs into leafs if they have no children.
34123                 // it's not very rational behaviour..
34124                 
34125                 if(!this.wasLeaf && this.node.leaf){
34126                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34127                     delete this.c1;
34128                     delete this.c2;
34129                     this.wasLeaf = true;
34130                 }
34131             }
34132             var ecc = "x-tree-ec-icon "+cls;
34133             if(this.ecc != ecc){
34134                 this.ecNode.className = ecc;
34135                 this.ecc = ecc;
34136             }
34137         }
34138     },
34139
34140     getChildIndent : function(){
34141         if(!this.childIndent){
34142             var buf = [];
34143             var p = this.node;
34144             while(p){
34145                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34146                     if(!p.isLast()) {
34147                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34148                     } else {
34149                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34150                     }
34151                 }
34152                 p = p.parentNode;
34153             }
34154             this.childIndent = buf.join("");
34155         }
34156         return this.childIndent;
34157     },
34158
34159     renderIndent : function(){
34160         if(this.rendered){
34161             var indent = "";
34162             var p = this.node.parentNode;
34163             if(p){
34164                 indent = p.ui.getChildIndent();
34165             }
34166             if(this.indentMarkup != indent){ // don't rerender if not required
34167                 this.indentNode.innerHTML = indent;
34168                 this.indentMarkup = indent;
34169             }
34170             this.updateExpandIcon();
34171         }
34172     }
34173 };
34174
34175 Roo.tree.RootTreeNodeUI = function(){
34176     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34177 };
34178 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34179     render : function(){
34180         if(!this.rendered){
34181             var targetNode = this.node.ownerTree.innerCt.dom;
34182             this.node.expanded = true;
34183             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34184             this.wrap = this.ctNode = targetNode.firstChild;
34185         }
34186     },
34187     collapse : function(){
34188     },
34189     expand : function(){
34190     }
34191 });/*
34192  * Based on:
34193  * Ext JS Library 1.1.1
34194  * Copyright(c) 2006-2007, Ext JS, LLC.
34195  *
34196  * Originally Released Under LGPL - original licence link has changed is not relivant.
34197  *
34198  * Fork - LGPL
34199  * <script type="text/javascript">
34200  */
34201 /**
34202  * @class Roo.tree.TreeLoader
34203  * @extends Roo.util.Observable
34204  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34205  * nodes from a specified URL. The response must be a javascript Array definition
34206  * who's elements are node definition objects. eg:
34207  * <pre><code>
34208 {  success : true,
34209    data :      [
34210    
34211     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34212     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34213     ]
34214 }
34215
34216
34217 </code></pre>
34218  * <br><br>
34219  * The old style respose with just an array is still supported, but not recommended.
34220  * <br><br>
34221  *
34222  * A server request is sent, and child nodes are loaded only when a node is expanded.
34223  * The loading node's id is passed to the server under the parameter name "node" to
34224  * enable the server to produce the correct child nodes.
34225  * <br><br>
34226  * To pass extra parameters, an event handler may be attached to the "beforeload"
34227  * event, and the parameters specified in the TreeLoader's baseParams property:
34228  * <pre><code>
34229     myTreeLoader.on("beforeload", function(treeLoader, node) {
34230         this.baseParams.category = node.attributes.category;
34231     }, this);
34232 </code></pre><
34233  * This would pass an HTTP parameter called "category" to the server containing
34234  * the value of the Node's "category" attribute.
34235  * @constructor
34236  * Creates a new Treeloader.
34237  * @param {Object} config A config object containing config properties.
34238  */
34239 Roo.tree.TreeLoader = function(config){
34240     this.baseParams = {};
34241     this.requestMethod = "POST";
34242     Roo.apply(this, config);
34243
34244     this.addEvents({
34245     
34246         /**
34247          * @event beforeload
34248          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34249          * @param {Object} This TreeLoader object.
34250          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34251          * @param {Object} callback The callback function specified in the {@link #load} call.
34252          */
34253         beforeload : true,
34254         /**
34255          * @event load
34256          * Fires when the node has been successfuly loaded.
34257          * @param {Object} This TreeLoader object.
34258          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34259          * @param {Object} response The response object containing the data from the server.
34260          */
34261         load : true,
34262         /**
34263          * @event loadexception
34264          * Fires if the network request failed.
34265          * @param {Object} This TreeLoader object.
34266          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34267          * @param {Object} response The response object containing the data from the server.
34268          */
34269         loadexception : true,
34270         /**
34271          * @event create
34272          * Fires before a node is created, enabling you to return custom Node types 
34273          * @param {Object} This TreeLoader object.
34274          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34275          */
34276         create : true
34277     });
34278
34279     Roo.tree.TreeLoader.superclass.constructor.call(this);
34280 };
34281
34282 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34283     /**
34284     * @cfg {String} dataUrl The URL from which to request a Json string which
34285     * specifies an array of node definition object representing the child nodes
34286     * to be loaded.
34287     */
34288     /**
34289     * @cfg {String} requestMethod either GET or POST
34290     * defaults to POST (due to BC)
34291     * to be loaded.
34292     */
34293     /**
34294     * @cfg {Object} baseParams (optional) An object containing properties which
34295     * specify HTTP parameters to be passed to each request for child nodes.
34296     */
34297     /**
34298     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34299     * created by this loader. If the attributes sent by the server have an attribute in this object,
34300     * they take priority.
34301     */
34302     /**
34303     * @cfg {Object} uiProviders (optional) An object containing properties which
34304     * 
34305     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34306     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34307     * <i>uiProvider</i> attribute of a returned child node is a string rather
34308     * than a reference to a TreeNodeUI implementation, this that string value
34309     * is used as a property name in the uiProviders object. You can define the provider named
34310     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34311     */
34312     uiProviders : {},
34313
34314     /**
34315     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34316     * child nodes before loading.
34317     */
34318     clearOnLoad : true,
34319
34320     /**
34321     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34322     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34323     * Grid query { data : [ .....] }
34324     */
34325     
34326     root : false,
34327      /**
34328     * @cfg {String} queryParam (optional) 
34329     * Name of the query as it will be passed on the querystring (defaults to 'node')
34330     * eg. the request will be ?node=[id]
34331     */
34332     
34333     
34334     queryParam: false,
34335     
34336     /**
34337      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34338      * This is called automatically when a node is expanded, but may be used to reload
34339      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34340      * @param {Roo.tree.TreeNode} node
34341      * @param {Function} callback
34342      */
34343     load : function(node, callback){
34344         if(this.clearOnLoad){
34345             while(node.firstChild){
34346                 node.removeChild(node.firstChild);
34347             }
34348         }
34349         if(node.attributes.children){ // preloaded json children
34350             var cs = node.attributes.children;
34351             for(var i = 0, len = cs.length; i < len; i++){
34352                 node.appendChild(this.createNode(cs[i]));
34353             }
34354             if(typeof callback == "function"){
34355                 callback();
34356             }
34357         }else if(this.dataUrl){
34358             this.requestData(node, callback);
34359         }
34360     },
34361
34362     getParams: function(node){
34363         var buf = [], bp = this.baseParams;
34364         for(var key in bp){
34365             if(typeof bp[key] != "function"){
34366                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34367             }
34368         }
34369         var n = this.queryParam === false ? 'node' : this.queryParam;
34370         buf.push(n + "=", encodeURIComponent(node.id));
34371         return buf.join("");
34372     },
34373
34374     requestData : function(node, callback){
34375         if(this.fireEvent("beforeload", this, node, callback) !== false){
34376             this.transId = Roo.Ajax.request({
34377                 method:this.requestMethod,
34378                 url: this.dataUrl||this.url,
34379                 success: this.handleResponse,
34380                 failure: this.handleFailure,
34381                 scope: this,
34382                 argument: {callback: callback, node: node},
34383                 params: this.getParams(node)
34384             });
34385         }else{
34386             // if the load is cancelled, make sure we notify
34387             // the node that we are done
34388             if(typeof callback == "function"){
34389                 callback();
34390             }
34391         }
34392     },
34393
34394     isLoading : function(){
34395         return this.transId ? true : false;
34396     },
34397
34398     abort : function(){
34399         if(this.isLoading()){
34400             Roo.Ajax.abort(this.transId);
34401         }
34402     },
34403
34404     // private
34405     createNode : function(attr)
34406     {
34407         // apply baseAttrs, nice idea Corey!
34408         if(this.baseAttrs){
34409             Roo.applyIf(attr, this.baseAttrs);
34410         }
34411         if(this.applyLoader !== false){
34412             attr.loader = this;
34413         }
34414         // uiProvider = depreciated..
34415         
34416         if(typeof(attr.uiProvider) == 'string'){
34417            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34418                 /**  eval:var:attr */ eval(attr.uiProvider);
34419         }
34420         if(typeof(this.uiProviders['default']) != 'undefined') {
34421             attr.uiProvider = this.uiProviders['default'];
34422         }
34423         
34424         this.fireEvent('create', this, attr);
34425         
34426         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34427         return(attr.leaf ?
34428                         new Roo.tree.TreeNode(attr) :
34429                         new Roo.tree.AsyncTreeNode(attr));
34430     },
34431
34432     processResponse : function(response, node, callback)
34433     {
34434         var json = response.responseText;
34435         try {
34436             
34437             var o = Roo.decode(json);
34438             
34439             if (this.root === false && typeof(o.success) != undefined) {
34440                 this.root = 'data'; // the default behaviour for list like data..
34441                 }
34442                 
34443             if (this.root !== false &&  !o.success) {
34444                 // it's a failure condition.
34445                 var a = response.argument;
34446                 this.fireEvent("loadexception", this, a.node, response);
34447                 Roo.log("Load failed - should have a handler really");
34448                 return;
34449             }
34450             
34451             
34452             
34453             if (this.root !== false) {
34454                  o = o[this.root];
34455             }
34456             
34457             for(var i = 0, len = o.length; i < len; i++){
34458                 var n = this.createNode(o[i]);
34459                 if(n){
34460                     node.appendChild(n);
34461                 }
34462             }
34463             if(typeof callback == "function"){
34464                 callback(this, node);
34465             }
34466         }catch(e){
34467             this.handleFailure(response);
34468         }
34469     },
34470
34471     handleResponse : function(response){
34472         this.transId = false;
34473         var a = response.argument;
34474         this.processResponse(response, a.node, a.callback);
34475         this.fireEvent("load", this, a.node, response);
34476     },
34477
34478     handleFailure : function(response)
34479     {
34480         // should handle failure better..
34481         this.transId = false;
34482         var a = response.argument;
34483         this.fireEvent("loadexception", this, a.node, response);
34484         if(typeof a.callback == "function"){
34485             a.callback(this, a.node);
34486         }
34487     }
34488 });/*
34489  * Based on:
34490  * Ext JS Library 1.1.1
34491  * Copyright(c) 2006-2007, Ext JS, LLC.
34492  *
34493  * Originally Released Under LGPL - original licence link has changed is not relivant.
34494  *
34495  * Fork - LGPL
34496  * <script type="text/javascript">
34497  */
34498
34499 /**
34500 * @class Roo.tree.TreeFilter
34501 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34502 * @param {TreePanel} tree
34503 * @param {Object} config (optional)
34504  */
34505 Roo.tree.TreeFilter = function(tree, config){
34506     this.tree = tree;
34507     this.filtered = {};
34508     Roo.apply(this, config);
34509 };
34510
34511 Roo.tree.TreeFilter.prototype = {
34512     clearBlank:false,
34513     reverse:false,
34514     autoClear:false,
34515     remove:false,
34516
34517      /**
34518      * Filter the data by a specific attribute.
34519      * @param {String/RegExp} value Either string that the attribute value
34520      * should start with or a RegExp to test against the attribute
34521      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34522      * @param {TreeNode} startNode (optional) The node to start the filter at.
34523      */
34524     filter : function(value, attr, startNode){
34525         attr = attr || "text";
34526         var f;
34527         if(typeof value == "string"){
34528             var vlen = value.length;
34529             // auto clear empty filter
34530             if(vlen == 0 && this.clearBlank){
34531                 this.clear();
34532                 return;
34533             }
34534             value = value.toLowerCase();
34535             f = function(n){
34536                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34537             };
34538         }else if(value.exec){ // regex?
34539             f = function(n){
34540                 return value.test(n.attributes[attr]);
34541             };
34542         }else{
34543             throw 'Illegal filter type, must be string or regex';
34544         }
34545         this.filterBy(f, null, startNode);
34546         },
34547
34548     /**
34549      * Filter by a function. The passed function will be called with each
34550      * node in the tree (or from the startNode). If the function returns true, the node is kept
34551      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34552      * @param {Function} fn The filter function
34553      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34554      */
34555     filterBy : function(fn, scope, startNode){
34556         startNode = startNode || this.tree.root;
34557         if(this.autoClear){
34558             this.clear();
34559         }
34560         var af = this.filtered, rv = this.reverse;
34561         var f = function(n){
34562             if(n == startNode){
34563                 return true;
34564             }
34565             if(af[n.id]){
34566                 return false;
34567             }
34568             var m = fn.call(scope || n, n);
34569             if(!m || rv){
34570                 af[n.id] = n;
34571                 n.ui.hide();
34572                 return false;
34573             }
34574             return true;
34575         };
34576         startNode.cascade(f);
34577         if(this.remove){
34578            for(var id in af){
34579                if(typeof id != "function"){
34580                    var n = af[id];
34581                    if(n && n.parentNode){
34582                        n.parentNode.removeChild(n);
34583                    }
34584                }
34585            }
34586         }
34587     },
34588
34589     /**
34590      * Clears the current filter. Note: with the "remove" option
34591      * set a filter cannot be cleared.
34592      */
34593     clear : function(){
34594         var t = this.tree;
34595         var af = this.filtered;
34596         for(var id in af){
34597             if(typeof id != "function"){
34598                 var n = af[id];
34599                 if(n){
34600                     n.ui.show();
34601                 }
34602             }
34603         }
34604         this.filtered = {};
34605     }
34606 };
34607 /*
34608  * Based on:
34609  * Ext JS Library 1.1.1
34610  * Copyright(c) 2006-2007, Ext JS, LLC.
34611  *
34612  * Originally Released Under LGPL - original licence link has changed is not relivant.
34613  *
34614  * Fork - LGPL
34615  * <script type="text/javascript">
34616  */
34617  
34618
34619 /**
34620  * @class Roo.tree.TreeSorter
34621  * Provides sorting of nodes in a TreePanel
34622  * 
34623  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34624  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34625  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34626  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34627  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34628  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34629  * @constructor
34630  * @param {TreePanel} tree
34631  * @param {Object} config
34632  */
34633 Roo.tree.TreeSorter = function(tree, config){
34634     Roo.apply(this, config);
34635     tree.on("beforechildrenrendered", this.doSort, this);
34636     tree.on("append", this.updateSort, this);
34637     tree.on("insert", this.updateSort, this);
34638     
34639     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34640     var p = this.property || "text";
34641     var sortType = this.sortType;
34642     var fs = this.folderSort;
34643     var cs = this.caseSensitive === true;
34644     var leafAttr = this.leafAttr || 'leaf';
34645
34646     this.sortFn = function(n1, n2){
34647         if(fs){
34648             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34649                 return 1;
34650             }
34651             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34652                 return -1;
34653             }
34654         }
34655         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34656         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34657         if(v1 < v2){
34658                         return dsc ? +1 : -1;
34659                 }else if(v1 > v2){
34660                         return dsc ? -1 : +1;
34661         }else{
34662                 return 0;
34663         }
34664     };
34665 };
34666
34667 Roo.tree.TreeSorter.prototype = {
34668     doSort : function(node){
34669         node.sort(this.sortFn);
34670     },
34671     
34672     compareNodes : function(n1, n2){
34673         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34674     },
34675     
34676     updateSort : function(tree, node){
34677         if(node.childrenRendered){
34678             this.doSort.defer(1, this, [node]);
34679         }
34680     }
34681 };/*
34682  * Based on:
34683  * Ext JS Library 1.1.1
34684  * Copyright(c) 2006-2007, Ext JS, LLC.
34685  *
34686  * Originally Released Under LGPL - original licence link has changed is not relivant.
34687  *
34688  * Fork - LGPL
34689  * <script type="text/javascript">
34690  */
34691
34692 if(Roo.dd.DropZone){
34693     
34694 Roo.tree.TreeDropZone = function(tree, config){
34695     this.allowParentInsert = false;
34696     this.allowContainerDrop = false;
34697     this.appendOnly = false;
34698     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34699     this.tree = tree;
34700     this.lastInsertClass = "x-tree-no-status";
34701     this.dragOverData = {};
34702 };
34703
34704 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34705     ddGroup : "TreeDD",
34706     scroll:  true,
34707     
34708     expandDelay : 1000,
34709     
34710     expandNode : function(node){
34711         if(node.hasChildNodes() && !node.isExpanded()){
34712             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34713         }
34714     },
34715     
34716     queueExpand : function(node){
34717         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34718     },
34719     
34720     cancelExpand : function(){
34721         if(this.expandProcId){
34722             clearTimeout(this.expandProcId);
34723             this.expandProcId = false;
34724         }
34725     },
34726     
34727     isValidDropPoint : function(n, pt, dd, e, data){
34728         if(!n || !data){ return false; }
34729         var targetNode = n.node;
34730         var dropNode = data.node;
34731         // default drop rules
34732         if(!(targetNode && targetNode.isTarget && pt)){
34733             return false;
34734         }
34735         if(pt == "append" && targetNode.allowChildren === false){
34736             return false;
34737         }
34738         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34739             return false;
34740         }
34741         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34742             return false;
34743         }
34744         // reuse the object
34745         var overEvent = this.dragOverData;
34746         overEvent.tree = this.tree;
34747         overEvent.target = targetNode;
34748         overEvent.data = data;
34749         overEvent.point = pt;
34750         overEvent.source = dd;
34751         overEvent.rawEvent = e;
34752         overEvent.dropNode = dropNode;
34753         overEvent.cancel = false;  
34754         var result = this.tree.fireEvent("nodedragover", overEvent);
34755         return overEvent.cancel === false && result !== false;
34756     },
34757     
34758     getDropPoint : function(e, n, dd)
34759     {
34760         var tn = n.node;
34761         if(tn.isRoot){
34762             return tn.allowChildren !== false ? "append" : false; // always append for root
34763         }
34764         var dragEl = n.ddel;
34765         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34766         var y = Roo.lib.Event.getPageY(e);
34767         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34768         
34769         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34770         var noAppend = tn.allowChildren === false;
34771         if(this.appendOnly || tn.parentNode.allowChildren === false){
34772             return noAppend ? false : "append";
34773         }
34774         var noBelow = false;
34775         if(!this.allowParentInsert){
34776             noBelow = tn.hasChildNodes() && tn.isExpanded();
34777         }
34778         var q = (b - t) / (noAppend ? 2 : 3);
34779         if(y >= t && y < (t + q)){
34780             return "above";
34781         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34782             return "below";
34783         }else{
34784             return "append";
34785         }
34786     },
34787     
34788     onNodeEnter : function(n, dd, e, data)
34789     {
34790         this.cancelExpand();
34791     },
34792     
34793     onNodeOver : function(n, dd, e, data)
34794     {
34795        
34796         var pt = this.getDropPoint(e, n, dd);
34797         var node = n.node;
34798         
34799         // auto node expand check
34800         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34801             this.queueExpand(node);
34802         }else if(pt != "append"){
34803             this.cancelExpand();
34804         }
34805         
34806         // set the insert point style on the target node
34807         var returnCls = this.dropNotAllowed;
34808         if(this.isValidDropPoint(n, pt, dd, e, data)){
34809            if(pt){
34810                var el = n.ddel;
34811                var cls;
34812                if(pt == "above"){
34813                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34814                    cls = "x-tree-drag-insert-above";
34815                }else if(pt == "below"){
34816                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34817                    cls = "x-tree-drag-insert-below";
34818                }else{
34819                    returnCls = "x-tree-drop-ok-append";
34820                    cls = "x-tree-drag-append";
34821                }
34822                if(this.lastInsertClass != cls){
34823                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34824                    this.lastInsertClass = cls;
34825                }
34826            }
34827        }
34828        return returnCls;
34829     },
34830     
34831     onNodeOut : function(n, dd, e, data){
34832         
34833         this.cancelExpand();
34834         this.removeDropIndicators(n);
34835     },
34836     
34837     onNodeDrop : function(n, dd, e, data){
34838         var point = this.getDropPoint(e, n, dd);
34839         var targetNode = n.node;
34840         targetNode.ui.startDrop();
34841         if(!this.isValidDropPoint(n, point, dd, e, data)){
34842             targetNode.ui.endDrop();
34843             return false;
34844         }
34845         // first try to find the drop node
34846         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34847         var dropEvent = {
34848             tree : this.tree,
34849             target: targetNode,
34850             data: data,
34851             point: point,
34852             source: dd,
34853             rawEvent: e,
34854             dropNode: dropNode,
34855             cancel: !dropNode   
34856         };
34857         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34858         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34859             targetNode.ui.endDrop();
34860             return false;
34861         }
34862         // allow target changing
34863         targetNode = dropEvent.target;
34864         if(point == "append" && !targetNode.isExpanded()){
34865             targetNode.expand(false, null, function(){
34866                 this.completeDrop(dropEvent);
34867             }.createDelegate(this));
34868         }else{
34869             this.completeDrop(dropEvent);
34870         }
34871         return true;
34872     },
34873     
34874     completeDrop : function(de){
34875         var ns = de.dropNode, p = de.point, t = de.target;
34876         if(!(ns instanceof Array)){
34877             ns = [ns];
34878         }
34879         var n;
34880         for(var i = 0, len = ns.length; i < len; i++){
34881             n = ns[i];
34882             if(p == "above"){
34883                 t.parentNode.insertBefore(n, t);
34884             }else if(p == "below"){
34885                 t.parentNode.insertBefore(n, t.nextSibling);
34886             }else{
34887                 t.appendChild(n);
34888             }
34889         }
34890         n.ui.focus();
34891         if(this.tree.hlDrop){
34892             n.ui.highlight();
34893         }
34894         t.ui.endDrop();
34895         this.tree.fireEvent("nodedrop", de);
34896     },
34897     
34898     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34899         if(this.tree.hlDrop){
34900             dropNode.ui.focus();
34901             dropNode.ui.highlight();
34902         }
34903         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34904     },
34905     
34906     getTree : function(){
34907         return this.tree;
34908     },
34909     
34910     removeDropIndicators : function(n){
34911         if(n && n.ddel){
34912             var el = n.ddel;
34913             Roo.fly(el).removeClass([
34914                     "x-tree-drag-insert-above",
34915                     "x-tree-drag-insert-below",
34916                     "x-tree-drag-append"]);
34917             this.lastInsertClass = "_noclass";
34918         }
34919     },
34920     
34921     beforeDragDrop : function(target, e, id){
34922         this.cancelExpand();
34923         return true;
34924     },
34925     
34926     afterRepair : function(data){
34927         if(data && Roo.enableFx){
34928             data.node.ui.highlight();
34929         }
34930         this.hideProxy();
34931     } 
34932     
34933 });
34934
34935 }
34936 /*
34937  * Based on:
34938  * Ext JS Library 1.1.1
34939  * Copyright(c) 2006-2007, Ext JS, LLC.
34940  *
34941  * Originally Released Under LGPL - original licence link has changed is not relivant.
34942  *
34943  * Fork - LGPL
34944  * <script type="text/javascript">
34945  */
34946  
34947
34948 if(Roo.dd.DragZone){
34949 Roo.tree.TreeDragZone = function(tree, config){
34950     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34951     this.tree = tree;
34952 };
34953
34954 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34955     ddGroup : "TreeDD",
34956    
34957     onBeforeDrag : function(data, e){
34958         var n = data.node;
34959         return n && n.draggable && !n.disabled;
34960     },
34961      
34962     
34963     onInitDrag : function(e){
34964         var data = this.dragData;
34965         this.tree.getSelectionModel().select(data.node);
34966         this.proxy.update("");
34967         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34968         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34969     },
34970     
34971     getRepairXY : function(e, data){
34972         return data.node.ui.getDDRepairXY();
34973     },
34974     
34975     onEndDrag : function(data, e){
34976         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34977         
34978         
34979     },
34980     
34981     onValidDrop : function(dd, e, id){
34982         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34983         this.hideProxy();
34984     },
34985     
34986     beforeInvalidDrop : function(e, id){
34987         // this scrolls the original position back into view
34988         var sm = this.tree.getSelectionModel();
34989         sm.clearSelections();
34990         sm.select(this.dragData.node);
34991     }
34992 });
34993 }/*
34994  * Based on:
34995  * Ext JS Library 1.1.1
34996  * Copyright(c) 2006-2007, Ext JS, LLC.
34997  *
34998  * Originally Released Under LGPL - original licence link has changed is not relivant.
34999  *
35000  * Fork - LGPL
35001  * <script type="text/javascript">
35002  */
35003 /**
35004  * @class Roo.tree.TreeEditor
35005  * @extends Roo.Editor
35006  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35007  * as the editor field.
35008  * @constructor
35009  * @param {Object} config (used to be the tree panel.)
35010  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35011  * 
35012  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35013  * @cfg {Roo.form.TextField|Object} field The field configuration
35014  *
35015  * 
35016  */
35017 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35018     var tree = config;
35019     var field;
35020     if (oldconfig) { // old style..
35021         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35022     } else {
35023         // new style..
35024         tree = config.tree;
35025         config.field = config.field  || {};
35026         config.field.xtype = 'TextField';
35027         field = Roo.factory(config.field, Roo.form);
35028     }
35029     config = config || {};
35030     
35031     
35032     this.addEvents({
35033         /**
35034          * @event beforenodeedit
35035          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35036          * false from the handler of this event.
35037          * @param {Editor} this
35038          * @param {Roo.tree.Node} node 
35039          */
35040         "beforenodeedit" : true
35041     });
35042     
35043     //Roo.log(config);
35044     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35045
35046     this.tree = tree;
35047
35048     tree.on('beforeclick', this.beforeNodeClick, this);
35049     tree.getTreeEl().on('mousedown', this.hide, this);
35050     this.on('complete', this.updateNode, this);
35051     this.on('beforestartedit', this.fitToTree, this);
35052     this.on('startedit', this.bindScroll, this, {delay:10});
35053     this.on('specialkey', this.onSpecialKey, this);
35054 };
35055
35056 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35057     /**
35058      * @cfg {String} alignment
35059      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35060      */
35061     alignment: "l-l",
35062     // inherit
35063     autoSize: false,
35064     /**
35065      * @cfg {Boolean} hideEl
35066      * True to hide the bound element while the editor is displayed (defaults to false)
35067      */
35068     hideEl : false,
35069     /**
35070      * @cfg {String} cls
35071      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35072      */
35073     cls: "x-small-editor x-tree-editor",
35074     /**
35075      * @cfg {Boolean} shim
35076      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35077      */
35078     shim:false,
35079     // inherit
35080     shadow:"frame",
35081     /**
35082      * @cfg {Number} maxWidth
35083      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35084      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35085      * scroll and client offsets into account prior to each edit.
35086      */
35087     maxWidth: 250,
35088
35089     editDelay : 350,
35090
35091     // private
35092     fitToTree : function(ed, el){
35093         var td = this.tree.getTreeEl().dom, nd = el.dom;
35094         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35095             td.scrollLeft = nd.offsetLeft;
35096         }
35097         var w = Math.min(
35098                 this.maxWidth,
35099                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35100         this.setSize(w, '');
35101         
35102         return this.fireEvent('beforenodeedit', this, this.editNode);
35103         
35104     },
35105
35106     // private
35107     triggerEdit : function(node){
35108         this.completeEdit();
35109         this.editNode = node;
35110         this.startEdit(node.ui.textNode, node.text);
35111     },
35112
35113     // private
35114     bindScroll : function(){
35115         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35116     },
35117
35118     // private
35119     beforeNodeClick : function(node, e){
35120         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35121         this.lastClick = new Date();
35122         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35123             e.stopEvent();
35124             this.triggerEdit(node);
35125             return false;
35126         }
35127         return true;
35128     },
35129
35130     // private
35131     updateNode : function(ed, value){
35132         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35133         this.editNode.setText(value);
35134     },
35135
35136     // private
35137     onHide : function(){
35138         Roo.tree.TreeEditor.superclass.onHide.call(this);
35139         if(this.editNode){
35140             this.editNode.ui.focus();
35141         }
35142     },
35143
35144     // private
35145     onSpecialKey : function(field, e){
35146         var k = e.getKey();
35147         if(k == e.ESC){
35148             e.stopEvent();
35149             this.cancelEdit();
35150         }else if(k == e.ENTER && !e.hasModifier()){
35151             e.stopEvent();
35152             this.completeEdit();
35153         }
35154     }
35155 });//<Script type="text/javascript">
35156 /*
35157  * Based on:
35158  * Ext JS Library 1.1.1
35159  * Copyright(c) 2006-2007, Ext JS, LLC.
35160  *
35161  * Originally Released Under LGPL - original licence link has changed is not relivant.
35162  *
35163  * Fork - LGPL
35164  * <script type="text/javascript">
35165  */
35166  
35167 /**
35168  * Not documented??? - probably should be...
35169  */
35170
35171 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35172     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35173     
35174     renderElements : function(n, a, targetNode, bulkRender){
35175         //consel.log("renderElements?");
35176         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35177
35178         var t = n.getOwnerTree();
35179         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35180         
35181         var cols = t.columns;
35182         var bw = t.borderWidth;
35183         var c = cols[0];
35184         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35185          var cb = typeof a.checked == "boolean";
35186         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35187         var colcls = 'x-t-' + tid + '-c0';
35188         var buf = [
35189             '<li class="x-tree-node">',
35190             
35191                 
35192                 '<div class="x-tree-node-el ', a.cls,'">',
35193                     // extran...
35194                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35195                 
35196                 
35197                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35198                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35199                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35200                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35201                            (a.iconCls ? ' '+a.iconCls : ''),
35202                            '" unselectable="on" />',
35203                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35204                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35205                              
35206                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35207                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35208                             '<span unselectable="on" qtip="' + tx + '">',
35209                              tx,
35210                              '</span></a>' ,
35211                     '</div>',
35212                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35213                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35214                  ];
35215         for(var i = 1, len = cols.length; i < len; i++){
35216             c = cols[i];
35217             colcls = 'x-t-' + tid + '-c' +i;
35218             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35219             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35220                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35221                       "</div>");
35222          }
35223          
35224          buf.push(
35225             '</a>',
35226             '<div class="x-clear"></div></div>',
35227             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35228             "</li>");
35229         
35230         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35231             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35232                                 n.nextSibling.ui.getEl(), buf.join(""));
35233         }else{
35234             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35235         }
35236         var el = this.wrap.firstChild;
35237         this.elRow = el;
35238         this.elNode = el.firstChild;
35239         this.ranchor = el.childNodes[1];
35240         this.ctNode = this.wrap.childNodes[1];
35241         var cs = el.firstChild.childNodes;
35242         this.indentNode = cs[0];
35243         this.ecNode = cs[1];
35244         this.iconNode = cs[2];
35245         var index = 3;
35246         if(cb){
35247             this.checkbox = cs[3];
35248             index++;
35249         }
35250         this.anchor = cs[index];
35251         
35252         this.textNode = cs[index].firstChild;
35253         
35254         //el.on("click", this.onClick, this);
35255         //el.on("dblclick", this.onDblClick, this);
35256         
35257         
35258        // console.log(this);
35259     },
35260     initEvents : function(){
35261         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35262         
35263             
35264         var a = this.ranchor;
35265
35266         var el = Roo.get(a);
35267
35268         if(Roo.isOpera){ // opera render bug ignores the CSS
35269             el.setStyle("text-decoration", "none");
35270         }
35271
35272         el.on("click", this.onClick, this);
35273         el.on("dblclick", this.onDblClick, this);
35274         el.on("contextmenu", this.onContextMenu, this);
35275         
35276     },
35277     
35278     /*onSelectedChange : function(state){
35279         if(state){
35280             this.focus();
35281             this.addClass("x-tree-selected");
35282         }else{
35283             //this.blur();
35284             this.removeClass("x-tree-selected");
35285         }
35286     },*/
35287     addClass : function(cls){
35288         if(this.elRow){
35289             Roo.fly(this.elRow).addClass(cls);
35290         }
35291         
35292     },
35293     
35294     
35295     removeClass : function(cls){
35296         if(this.elRow){
35297             Roo.fly(this.elRow).removeClass(cls);
35298         }
35299     }
35300
35301     
35302     
35303 });//<Script type="text/javascript">
35304
35305 /*
35306  * Based on:
35307  * Ext JS Library 1.1.1
35308  * Copyright(c) 2006-2007, Ext JS, LLC.
35309  *
35310  * Originally Released Under LGPL - original licence link has changed is not relivant.
35311  *
35312  * Fork - LGPL
35313  * <script type="text/javascript">
35314  */
35315  
35316
35317 /**
35318  * @class Roo.tree.ColumnTree
35319  * @extends Roo.data.TreePanel
35320  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35321  * @cfg {int} borderWidth  compined right/left border allowance
35322  * @constructor
35323  * @param {String/HTMLElement/Element} el The container element
35324  * @param {Object} config
35325  */
35326 Roo.tree.ColumnTree =  function(el, config)
35327 {
35328    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35329    this.addEvents({
35330         /**
35331         * @event resize
35332         * Fire this event on a container when it resizes
35333         * @param {int} w Width
35334         * @param {int} h Height
35335         */
35336        "resize" : true
35337     });
35338     this.on('resize', this.onResize, this);
35339 };
35340
35341 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35342     //lines:false,
35343     
35344     
35345     borderWidth: Roo.isBorderBox ? 0 : 2, 
35346     headEls : false,
35347     
35348     render : function(){
35349         // add the header.....
35350        
35351         Roo.tree.ColumnTree.superclass.render.apply(this);
35352         
35353         this.el.addClass('x-column-tree');
35354         
35355         this.headers = this.el.createChild(
35356             {cls:'x-tree-headers'},this.innerCt.dom);
35357    
35358         var cols = this.columns, c;
35359         var totalWidth = 0;
35360         this.headEls = [];
35361         var  len = cols.length;
35362         for(var i = 0; i < len; i++){
35363              c = cols[i];
35364              totalWidth += c.width;
35365             this.headEls.push(this.headers.createChild({
35366                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35367                  cn: {
35368                      cls:'x-tree-hd-text',
35369                      html: c.header
35370                  },
35371                  style:'width:'+(c.width-this.borderWidth)+'px;'
35372              }));
35373         }
35374         this.headers.createChild({cls:'x-clear'});
35375         // prevent floats from wrapping when clipped
35376         this.headers.setWidth(totalWidth);
35377         //this.innerCt.setWidth(totalWidth);
35378         this.innerCt.setStyle({ overflow: 'auto' });
35379         this.onResize(this.width, this.height);
35380              
35381         
35382     },
35383     onResize : function(w,h)
35384     {
35385         this.height = h;
35386         this.width = w;
35387         // resize cols..
35388         this.innerCt.setWidth(this.width);
35389         this.innerCt.setHeight(this.height-20);
35390         
35391         // headers...
35392         var cols = this.columns, c;
35393         var totalWidth = 0;
35394         var expEl = false;
35395         var len = cols.length;
35396         for(var i = 0; i < len; i++){
35397             c = cols[i];
35398             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35399                 // it's the expander..
35400                 expEl  = this.headEls[i];
35401                 continue;
35402             }
35403             totalWidth += c.width;
35404             
35405         }
35406         if (expEl) {
35407             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35408         }
35409         this.headers.setWidth(w-20);
35410
35411         
35412         
35413         
35414     }
35415 });
35416 /*
35417  * Based on:
35418  * Ext JS Library 1.1.1
35419  * Copyright(c) 2006-2007, Ext JS, LLC.
35420  *
35421  * Originally Released Under LGPL - original licence link has changed is not relivant.
35422  *
35423  * Fork - LGPL
35424  * <script type="text/javascript">
35425  */
35426  
35427 /**
35428  * @class Roo.menu.Menu
35429  * @extends Roo.util.Observable
35430  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35431  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35432  * @constructor
35433  * Creates a new Menu
35434  * @param {Object} config Configuration options
35435  */
35436 Roo.menu.Menu = function(config){
35437     Roo.apply(this, config);
35438     this.id = this.id || Roo.id();
35439     this.addEvents({
35440         /**
35441          * @event beforeshow
35442          * Fires before this menu is displayed
35443          * @param {Roo.menu.Menu} this
35444          */
35445         beforeshow : true,
35446         /**
35447          * @event beforehide
35448          * Fires before this menu is hidden
35449          * @param {Roo.menu.Menu} this
35450          */
35451         beforehide : true,
35452         /**
35453          * @event show
35454          * Fires after this menu is displayed
35455          * @param {Roo.menu.Menu} this
35456          */
35457         show : true,
35458         /**
35459          * @event hide
35460          * Fires after this menu is hidden
35461          * @param {Roo.menu.Menu} this
35462          */
35463         hide : true,
35464         /**
35465          * @event click
35466          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35467          * @param {Roo.menu.Menu} this
35468          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35469          * @param {Roo.EventObject} e
35470          */
35471         click : true,
35472         /**
35473          * @event mouseover
35474          * Fires when the mouse is hovering over this menu
35475          * @param {Roo.menu.Menu} this
35476          * @param {Roo.EventObject} e
35477          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35478          */
35479         mouseover : true,
35480         /**
35481          * @event mouseout
35482          * Fires when the mouse exits this menu
35483          * @param {Roo.menu.Menu} this
35484          * @param {Roo.EventObject} e
35485          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35486          */
35487         mouseout : true,
35488         /**
35489          * @event itemclick
35490          * Fires when a menu item contained in this menu is clicked
35491          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35492          * @param {Roo.EventObject} e
35493          */
35494         itemclick: true
35495     });
35496     if (this.registerMenu) {
35497         Roo.menu.MenuMgr.register(this);
35498     }
35499     
35500     var mis = this.items;
35501     this.items = new Roo.util.MixedCollection();
35502     if(mis){
35503         this.add.apply(this, mis);
35504     }
35505 };
35506
35507 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35508     /**
35509      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35510      */
35511     minWidth : 120,
35512     /**
35513      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35514      * for bottom-right shadow (defaults to "sides")
35515      */
35516     shadow : "sides",
35517     /**
35518      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35519      * this menu (defaults to "tl-tr?")
35520      */
35521     subMenuAlign : "tl-tr?",
35522     /**
35523      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35524      * relative to its element of origin (defaults to "tl-bl?")
35525      */
35526     defaultAlign : "tl-bl?",
35527     /**
35528      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35529      */
35530     allowOtherMenus : false,
35531     /**
35532      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35533      */
35534     registerMenu : true,
35535
35536     hidden:true,
35537
35538     // private
35539     render : function(){
35540         if(this.el){
35541             return;
35542         }
35543         var el = this.el = new Roo.Layer({
35544             cls: "x-menu",
35545             shadow:this.shadow,
35546             constrain: false,
35547             parentEl: this.parentEl || document.body,
35548             zindex:15000
35549         });
35550
35551         this.keyNav = new Roo.menu.MenuNav(this);
35552
35553         if(this.plain){
35554             el.addClass("x-menu-plain");
35555         }
35556         if(this.cls){
35557             el.addClass(this.cls);
35558         }
35559         // generic focus element
35560         this.focusEl = el.createChild({
35561             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35562         });
35563         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35564         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35565         
35566         ul.on("mouseover", this.onMouseOver, this);
35567         ul.on("mouseout", this.onMouseOut, this);
35568         this.items.each(function(item){
35569             if (item.hidden) {
35570                 return;
35571             }
35572             
35573             var li = document.createElement("li");
35574             li.className = "x-menu-list-item";
35575             ul.dom.appendChild(li);
35576             item.render(li, this);
35577         }, this);
35578         this.ul = ul;
35579         this.autoWidth();
35580     },
35581
35582     // private
35583     autoWidth : function(){
35584         var el = this.el, ul = this.ul;
35585         if(!el){
35586             return;
35587         }
35588         var w = this.width;
35589         if(w){
35590             el.setWidth(w);
35591         }else if(Roo.isIE){
35592             el.setWidth(this.minWidth);
35593             var t = el.dom.offsetWidth; // force recalc
35594             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35595         }
35596     },
35597
35598     // private
35599     delayAutoWidth : function(){
35600         if(this.rendered){
35601             if(!this.awTask){
35602                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35603             }
35604             this.awTask.delay(20);
35605         }
35606     },
35607
35608     // private
35609     findTargetItem : function(e){
35610         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35611         if(t && t.menuItemId){
35612             return this.items.get(t.menuItemId);
35613         }
35614     },
35615
35616     // private
35617     onClick : function(e){
35618         Roo.log("menu.onClick");
35619         var t = this.findTargetItem(e);
35620         if(!t){
35621             return;
35622         }
35623         Roo.log(e);
35624         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35625             if(t == this.activeItem && t.shouldDeactivate(e)){
35626                 this.activeItem.deactivate();
35627                 delete this.activeItem;
35628                 return;
35629             }
35630             if(t.canActivate){
35631                 this.setActiveItem(t, true);
35632             }
35633             return;
35634             
35635             
35636         }
35637         
35638         t.onClick(e);
35639         this.fireEvent("click", this, t, e);
35640     },
35641
35642     // private
35643     setActiveItem : function(item, autoExpand){
35644         if(item != this.activeItem){
35645             if(this.activeItem){
35646                 this.activeItem.deactivate();
35647             }
35648             this.activeItem = item;
35649             item.activate(autoExpand);
35650         }else if(autoExpand){
35651             item.expandMenu();
35652         }
35653     },
35654
35655     // private
35656     tryActivate : function(start, step){
35657         var items = this.items;
35658         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35659             var item = items.get(i);
35660             if(!item.disabled && item.canActivate){
35661                 this.setActiveItem(item, false);
35662                 return item;
35663             }
35664         }
35665         return false;
35666     },
35667
35668     // private
35669     onMouseOver : function(e){
35670         var t;
35671         if(t = this.findTargetItem(e)){
35672             if(t.canActivate && !t.disabled){
35673                 this.setActiveItem(t, true);
35674             }
35675         }
35676         this.fireEvent("mouseover", this, e, t);
35677     },
35678
35679     // private
35680     onMouseOut : function(e){
35681         var t;
35682         if(t = this.findTargetItem(e)){
35683             if(t == this.activeItem && t.shouldDeactivate(e)){
35684                 this.activeItem.deactivate();
35685                 delete this.activeItem;
35686             }
35687         }
35688         this.fireEvent("mouseout", this, e, t);
35689     },
35690
35691     /**
35692      * Read-only.  Returns true if the menu is currently displayed, else false.
35693      * @type Boolean
35694      */
35695     isVisible : function(){
35696         return this.el && !this.hidden;
35697     },
35698
35699     /**
35700      * Displays this menu relative to another element
35701      * @param {String/HTMLElement/Roo.Element} element The element to align to
35702      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35703      * the element (defaults to this.defaultAlign)
35704      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35705      */
35706     show : function(el, pos, parentMenu){
35707         this.parentMenu = parentMenu;
35708         if(!this.el){
35709             this.render();
35710         }
35711         this.fireEvent("beforeshow", this);
35712         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35713     },
35714
35715     /**
35716      * Displays this menu at a specific xy position
35717      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35718      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35719      */
35720     showAt : function(xy, parentMenu, /* private: */_e){
35721         this.parentMenu = parentMenu;
35722         if(!this.el){
35723             this.render();
35724         }
35725         if(_e !== false){
35726             this.fireEvent("beforeshow", this);
35727             xy = this.el.adjustForConstraints(xy);
35728         }
35729         this.el.setXY(xy);
35730         this.el.show();
35731         this.hidden = false;
35732         this.focus();
35733         this.fireEvent("show", this);
35734     },
35735
35736     focus : function(){
35737         if(!this.hidden){
35738             this.doFocus.defer(50, this);
35739         }
35740     },
35741
35742     doFocus : function(){
35743         if(!this.hidden){
35744             this.focusEl.focus();
35745         }
35746     },
35747
35748     /**
35749      * Hides this menu and optionally all parent menus
35750      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35751      */
35752     hide : function(deep){
35753         if(this.el && this.isVisible()){
35754             this.fireEvent("beforehide", this);
35755             if(this.activeItem){
35756                 this.activeItem.deactivate();
35757                 this.activeItem = null;
35758             }
35759             this.el.hide();
35760             this.hidden = true;
35761             this.fireEvent("hide", this);
35762         }
35763         if(deep === true && this.parentMenu){
35764             this.parentMenu.hide(true);
35765         }
35766     },
35767
35768     /**
35769      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35770      * Any of the following are valid:
35771      * <ul>
35772      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35773      * <li>An HTMLElement object which will be converted to a menu item</li>
35774      * <li>A menu item config object that will be created as a new menu item</li>
35775      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35776      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35777      * </ul>
35778      * Usage:
35779      * <pre><code>
35780 // Create the menu
35781 var menu = new Roo.menu.Menu();
35782
35783 // Create a menu item to add by reference
35784 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35785
35786 // Add a bunch of items at once using different methods.
35787 // Only the last item added will be returned.
35788 var item = menu.add(
35789     menuItem,                // add existing item by ref
35790     'Dynamic Item',          // new TextItem
35791     '-',                     // new separator
35792     { text: 'Config Item' }  // new item by config
35793 );
35794 </code></pre>
35795      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35796      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35797      */
35798     add : function(){
35799         var a = arguments, l = a.length, item;
35800         for(var i = 0; i < l; i++){
35801             var el = a[i];
35802             if ((typeof(el) == "object") && el.xtype && el.xns) {
35803                 el = Roo.factory(el, Roo.menu);
35804             }
35805             
35806             if(el.render){ // some kind of Item
35807                 item = this.addItem(el);
35808             }else if(typeof el == "string"){ // string
35809                 if(el == "separator" || el == "-"){
35810                     item = this.addSeparator();
35811                 }else{
35812                     item = this.addText(el);
35813                 }
35814             }else if(el.tagName || el.el){ // element
35815                 item = this.addElement(el);
35816             }else if(typeof el == "object"){ // must be menu item config?
35817                 item = this.addMenuItem(el);
35818             }
35819         }
35820         return item;
35821     },
35822
35823     /**
35824      * Returns this menu's underlying {@link Roo.Element} object
35825      * @return {Roo.Element} The element
35826      */
35827     getEl : function(){
35828         if(!this.el){
35829             this.render();
35830         }
35831         return this.el;
35832     },
35833
35834     /**
35835      * Adds a separator bar to the menu
35836      * @return {Roo.menu.Item} The menu item that was added
35837      */
35838     addSeparator : function(){
35839         return this.addItem(new Roo.menu.Separator());
35840     },
35841
35842     /**
35843      * Adds an {@link Roo.Element} object to the menu
35844      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35845      * @return {Roo.menu.Item} The menu item that was added
35846      */
35847     addElement : function(el){
35848         return this.addItem(new Roo.menu.BaseItem(el));
35849     },
35850
35851     /**
35852      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35853      * @param {Roo.menu.Item} item The menu item to add
35854      * @return {Roo.menu.Item} The menu item that was added
35855      */
35856     addItem : function(item){
35857         this.items.add(item);
35858         if(this.ul){
35859             var li = document.createElement("li");
35860             li.className = "x-menu-list-item";
35861             this.ul.dom.appendChild(li);
35862             item.render(li, this);
35863             this.delayAutoWidth();
35864         }
35865         return item;
35866     },
35867
35868     /**
35869      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35870      * @param {Object} config A MenuItem config object
35871      * @return {Roo.menu.Item} The menu item that was added
35872      */
35873     addMenuItem : function(config){
35874         if(!(config instanceof Roo.menu.Item)){
35875             if(typeof config.checked == "boolean"){ // must be check menu item config?
35876                 config = new Roo.menu.CheckItem(config);
35877             }else{
35878                 config = new Roo.menu.Item(config);
35879             }
35880         }
35881         return this.addItem(config);
35882     },
35883
35884     /**
35885      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35886      * @param {String} text The text to display in the menu item
35887      * @return {Roo.menu.Item} The menu item that was added
35888      */
35889     addText : function(text){
35890         return this.addItem(new Roo.menu.TextItem({ text : text }));
35891     },
35892
35893     /**
35894      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35895      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35896      * @param {Roo.menu.Item} item The menu item to add
35897      * @return {Roo.menu.Item} The menu item that was added
35898      */
35899     insert : function(index, item){
35900         this.items.insert(index, item);
35901         if(this.ul){
35902             var li = document.createElement("li");
35903             li.className = "x-menu-list-item";
35904             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35905             item.render(li, this);
35906             this.delayAutoWidth();
35907         }
35908         return item;
35909     },
35910
35911     /**
35912      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35913      * @param {Roo.menu.Item} item The menu item to remove
35914      */
35915     remove : function(item){
35916         this.items.removeKey(item.id);
35917         item.destroy();
35918     },
35919
35920     /**
35921      * Removes and destroys all items in the menu
35922      */
35923     removeAll : function(){
35924         var f;
35925         while(f = this.items.first()){
35926             this.remove(f);
35927         }
35928     }
35929 });
35930
35931 // MenuNav is a private utility class used internally by the Menu
35932 Roo.menu.MenuNav = function(menu){
35933     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35934     this.scope = this.menu = menu;
35935 };
35936
35937 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35938     doRelay : function(e, h){
35939         var k = e.getKey();
35940         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35941             this.menu.tryActivate(0, 1);
35942             return false;
35943         }
35944         return h.call(this.scope || this, e, this.menu);
35945     },
35946
35947     up : function(e, m){
35948         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35949             m.tryActivate(m.items.length-1, -1);
35950         }
35951     },
35952
35953     down : function(e, m){
35954         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35955             m.tryActivate(0, 1);
35956         }
35957     },
35958
35959     right : function(e, m){
35960         if(m.activeItem){
35961             m.activeItem.expandMenu(true);
35962         }
35963     },
35964
35965     left : function(e, m){
35966         m.hide();
35967         if(m.parentMenu && m.parentMenu.activeItem){
35968             m.parentMenu.activeItem.activate();
35969         }
35970     },
35971
35972     enter : function(e, m){
35973         if(m.activeItem){
35974             e.stopPropagation();
35975             m.activeItem.onClick(e);
35976             m.fireEvent("click", this, m.activeItem);
35977             return true;
35978         }
35979     }
35980 });/*
35981  * Based on:
35982  * Ext JS Library 1.1.1
35983  * Copyright(c) 2006-2007, Ext JS, LLC.
35984  *
35985  * Originally Released Under LGPL - original licence link has changed is not relivant.
35986  *
35987  * Fork - LGPL
35988  * <script type="text/javascript">
35989  */
35990  
35991 /**
35992  * @class Roo.menu.MenuMgr
35993  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35994  * @singleton
35995  */
35996 Roo.menu.MenuMgr = function(){
35997    var menus, active, groups = {}, attached = false, lastShow = new Date();
35998
35999    // private - called when first menu is created
36000    function init(){
36001        menus = {};
36002        active = new Roo.util.MixedCollection();
36003        Roo.get(document).addKeyListener(27, function(){
36004            if(active.length > 0){
36005                hideAll();
36006            }
36007        });
36008    }
36009
36010    // private
36011    function hideAll(){
36012        if(active && active.length > 0){
36013            var c = active.clone();
36014            c.each(function(m){
36015                m.hide();
36016            });
36017        }
36018    }
36019
36020    // private
36021    function onHide(m){
36022        active.remove(m);
36023        if(active.length < 1){
36024            Roo.get(document).un("mousedown", onMouseDown);
36025            attached = false;
36026        }
36027    }
36028
36029    // private
36030    function onShow(m){
36031        var last = active.last();
36032        lastShow = new Date();
36033        active.add(m);
36034        if(!attached){
36035            Roo.get(document).on("mousedown", onMouseDown);
36036            attached = true;
36037        }
36038        if(m.parentMenu){
36039           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36040           m.parentMenu.activeChild = m;
36041        }else if(last && last.isVisible()){
36042           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36043        }
36044    }
36045
36046    // private
36047    function onBeforeHide(m){
36048        if(m.activeChild){
36049            m.activeChild.hide();
36050        }
36051        if(m.autoHideTimer){
36052            clearTimeout(m.autoHideTimer);
36053            delete m.autoHideTimer;
36054        }
36055    }
36056
36057    // private
36058    function onBeforeShow(m){
36059        var pm = m.parentMenu;
36060        if(!pm && !m.allowOtherMenus){
36061            hideAll();
36062        }else if(pm && pm.activeChild && active != m){
36063            pm.activeChild.hide();
36064        }
36065    }
36066
36067    // private
36068    function onMouseDown(e){
36069        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36070            hideAll();
36071        }
36072    }
36073
36074    // private
36075    function onBeforeCheck(mi, state){
36076        if(state){
36077            var g = groups[mi.group];
36078            for(var i = 0, l = g.length; i < l; i++){
36079                if(g[i] != mi){
36080                    g[i].setChecked(false);
36081                }
36082            }
36083        }
36084    }
36085
36086    return {
36087
36088        /**
36089         * Hides all menus that are currently visible
36090         */
36091        hideAll : function(){
36092             hideAll();  
36093        },
36094
36095        // private
36096        register : function(menu){
36097            if(!menus){
36098                init();
36099            }
36100            menus[menu.id] = menu;
36101            menu.on("beforehide", onBeforeHide);
36102            menu.on("hide", onHide);
36103            menu.on("beforeshow", onBeforeShow);
36104            menu.on("show", onShow);
36105            var g = menu.group;
36106            if(g && menu.events["checkchange"]){
36107                if(!groups[g]){
36108                    groups[g] = [];
36109                }
36110                groups[g].push(menu);
36111                menu.on("checkchange", onCheck);
36112            }
36113        },
36114
36115         /**
36116          * Returns a {@link Roo.menu.Menu} object
36117          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36118          * be used to generate and return a new Menu instance.
36119          */
36120        get : function(menu){
36121            if(typeof menu == "string"){ // menu id
36122                return menus[menu];
36123            }else if(menu.events){  // menu instance
36124                return menu;
36125            }else if(typeof menu.length == 'number'){ // array of menu items?
36126                return new Roo.menu.Menu({items:menu});
36127            }else{ // otherwise, must be a config
36128                return new Roo.menu.Menu(menu);
36129            }
36130        },
36131
36132        // private
36133        unregister : function(menu){
36134            delete menus[menu.id];
36135            menu.un("beforehide", onBeforeHide);
36136            menu.un("hide", onHide);
36137            menu.un("beforeshow", onBeforeShow);
36138            menu.un("show", onShow);
36139            var g = menu.group;
36140            if(g && menu.events["checkchange"]){
36141                groups[g].remove(menu);
36142                menu.un("checkchange", onCheck);
36143            }
36144        },
36145
36146        // private
36147        registerCheckable : function(menuItem){
36148            var g = menuItem.group;
36149            if(g){
36150                if(!groups[g]){
36151                    groups[g] = [];
36152                }
36153                groups[g].push(menuItem);
36154                menuItem.on("beforecheckchange", onBeforeCheck);
36155            }
36156        },
36157
36158        // private
36159        unregisterCheckable : function(menuItem){
36160            var g = menuItem.group;
36161            if(g){
36162                groups[g].remove(menuItem);
36163                menuItem.un("beforecheckchange", onBeforeCheck);
36164            }
36165        }
36166    };
36167 }();/*
36168  * Based on:
36169  * Ext JS Library 1.1.1
36170  * Copyright(c) 2006-2007, Ext JS, LLC.
36171  *
36172  * Originally Released Under LGPL - original licence link has changed is not relivant.
36173  *
36174  * Fork - LGPL
36175  * <script type="text/javascript">
36176  */
36177  
36178
36179 /**
36180  * @class Roo.menu.BaseItem
36181  * @extends Roo.Component
36182  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36183  * management and base configuration options shared by all menu components.
36184  * @constructor
36185  * Creates a new BaseItem
36186  * @param {Object} config Configuration options
36187  */
36188 Roo.menu.BaseItem = function(config){
36189     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36190
36191     this.addEvents({
36192         /**
36193          * @event click
36194          * Fires when this item is clicked
36195          * @param {Roo.menu.BaseItem} this
36196          * @param {Roo.EventObject} e
36197          */
36198         click: true,
36199         /**
36200          * @event activate
36201          * Fires when this item is activated
36202          * @param {Roo.menu.BaseItem} this
36203          */
36204         activate : true,
36205         /**
36206          * @event deactivate
36207          * Fires when this item is deactivated
36208          * @param {Roo.menu.BaseItem} this
36209          */
36210         deactivate : true
36211     });
36212
36213     if(this.handler){
36214         this.on("click", this.handler, this.scope, true);
36215     }
36216 };
36217
36218 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36219     /**
36220      * @cfg {Function} handler
36221      * A function that will handle the click event of this menu item (defaults to undefined)
36222      */
36223     /**
36224      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36225      */
36226     canActivate : false,
36227     
36228      /**
36229      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36230      */
36231     hidden: false,
36232     
36233     /**
36234      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36235      */
36236     activeClass : "x-menu-item-active",
36237     /**
36238      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36239      */
36240     hideOnClick : true,
36241     /**
36242      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36243      */
36244     hideDelay : 100,
36245
36246     // private
36247     ctype: "Roo.menu.BaseItem",
36248
36249     // private
36250     actionMode : "container",
36251
36252     // private
36253     render : function(container, parentMenu){
36254         this.parentMenu = parentMenu;
36255         Roo.menu.BaseItem.superclass.render.call(this, container);
36256         this.container.menuItemId = this.id;
36257     },
36258
36259     // private
36260     onRender : function(container, position){
36261         this.el = Roo.get(this.el);
36262         container.dom.appendChild(this.el.dom);
36263     },
36264
36265     // private
36266     onClick : function(e){
36267         if(!this.disabled && this.fireEvent("click", this, e) !== false
36268                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36269             this.handleClick(e);
36270         }else{
36271             e.stopEvent();
36272         }
36273     },
36274
36275     // private
36276     activate : function(){
36277         if(this.disabled){
36278             return false;
36279         }
36280         var li = this.container;
36281         li.addClass(this.activeClass);
36282         this.region = li.getRegion().adjust(2, 2, -2, -2);
36283         this.fireEvent("activate", this);
36284         return true;
36285     },
36286
36287     // private
36288     deactivate : function(){
36289         this.container.removeClass(this.activeClass);
36290         this.fireEvent("deactivate", this);
36291     },
36292
36293     // private
36294     shouldDeactivate : function(e){
36295         return !this.region || !this.region.contains(e.getPoint());
36296     },
36297
36298     // private
36299     handleClick : function(e){
36300         if(this.hideOnClick){
36301             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36302         }
36303     },
36304
36305     // private
36306     expandMenu : function(autoActivate){
36307         // do nothing
36308     },
36309
36310     // private
36311     hideMenu : function(){
36312         // do nothing
36313     }
36314 });/*
36315  * Based on:
36316  * Ext JS Library 1.1.1
36317  * Copyright(c) 2006-2007, Ext JS, LLC.
36318  *
36319  * Originally Released Under LGPL - original licence link has changed is not relivant.
36320  *
36321  * Fork - LGPL
36322  * <script type="text/javascript">
36323  */
36324  
36325 /**
36326  * @class Roo.menu.Adapter
36327  * @extends Roo.menu.BaseItem
36328  * 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.
36329  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36330  * @constructor
36331  * Creates a new Adapter
36332  * @param {Object} config Configuration options
36333  */
36334 Roo.menu.Adapter = function(component, config){
36335     Roo.menu.Adapter.superclass.constructor.call(this, config);
36336     this.component = component;
36337 };
36338 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36339     // private
36340     canActivate : true,
36341
36342     // private
36343     onRender : function(container, position){
36344         this.component.render(container);
36345         this.el = this.component.getEl();
36346     },
36347
36348     // private
36349     activate : function(){
36350         if(this.disabled){
36351             return false;
36352         }
36353         this.component.focus();
36354         this.fireEvent("activate", this);
36355         return true;
36356     },
36357
36358     // private
36359     deactivate : function(){
36360         this.fireEvent("deactivate", this);
36361     },
36362
36363     // private
36364     disable : function(){
36365         this.component.disable();
36366         Roo.menu.Adapter.superclass.disable.call(this);
36367     },
36368
36369     // private
36370     enable : function(){
36371         this.component.enable();
36372         Roo.menu.Adapter.superclass.enable.call(this);
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 /**
36386  * @class Roo.menu.TextItem
36387  * @extends Roo.menu.BaseItem
36388  * Adds a static text string to a menu, usually used as either a heading or group separator.
36389  * Note: old style constructor with text is still supported.
36390  * 
36391  * @constructor
36392  * Creates a new TextItem
36393  * @param {Object} cfg Configuration
36394  */
36395 Roo.menu.TextItem = function(cfg){
36396     if (typeof(cfg) == 'string') {
36397         this.text = cfg;
36398     } else {
36399         Roo.apply(this,cfg);
36400     }
36401     
36402     Roo.menu.TextItem.superclass.constructor.call(this);
36403 };
36404
36405 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36406     /**
36407      * @cfg {Boolean} text Text to show on item.
36408      */
36409     text : '',
36410     
36411     /**
36412      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36413      */
36414     hideOnClick : false,
36415     /**
36416      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36417      */
36418     itemCls : "x-menu-text",
36419
36420     // private
36421     onRender : function(){
36422         var s = document.createElement("span");
36423         s.className = this.itemCls;
36424         s.innerHTML = this.text;
36425         this.el = s;
36426         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36427     }
36428 });/*
36429  * Based on:
36430  * Ext JS Library 1.1.1
36431  * Copyright(c) 2006-2007, Ext JS, LLC.
36432  *
36433  * Originally Released Under LGPL - original licence link has changed is not relivant.
36434  *
36435  * Fork - LGPL
36436  * <script type="text/javascript">
36437  */
36438
36439 /**
36440  * @class Roo.menu.Separator
36441  * @extends Roo.menu.BaseItem
36442  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36443  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36444  * @constructor
36445  * @param {Object} config Configuration options
36446  */
36447 Roo.menu.Separator = function(config){
36448     Roo.menu.Separator.superclass.constructor.call(this, config);
36449 };
36450
36451 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36452     /**
36453      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36454      */
36455     itemCls : "x-menu-sep",
36456     /**
36457      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36458      */
36459     hideOnClick : false,
36460
36461     // private
36462     onRender : function(li){
36463         var s = document.createElement("span");
36464         s.className = this.itemCls;
36465         s.innerHTML = "&#160;";
36466         this.el = s;
36467         li.addClass("x-menu-sep-li");
36468         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36469     }
36470 });/*
36471  * Based on:
36472  * Ext JS Library 1.1.1
36473  * Copyright(c) 2006-2007, Ext JS, LLC.
36474  *
36475  * Originally Released Under LGPL - original licence link has changed is not relivant.
36476  *
36477  * Fork - LGPL
36478  * <script type="text/javascript">
36479  */
36480 /**
36481  * @class Roo.menu.Item
36482  * @extends Roo.menu.BaseItem
36483  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36484  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36485  * activation and click handling.
36486  * @constructor
36487  * Creates a new Item
36488  * @param {Object} config Configuration options
36489  */
36490 Roo.menu.Item = function(config){
36491     Roo.menu.Item.superclass.constructor.call(this, config);
36492     if(this.menu){
36493         this.menu = Roo.menu.MenuMgr.get(this.menu);
36494     }
36495 };
36496 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36497     
36498     /**
36499      * @cfg {String} text
36500      * The text to show on the menu item.
36501      */
36502     text: '',
36503      /**
36504      * @cfg {String} HTML to render in menu
36505      * The text to show on the menu item (HTML version).
36506      */
36507     html: '',
36508     /**
36509      * @cfg {String} icon
36510      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36511      */
36512     icon: undefined,
36513     /**
36514      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36515      */
36516     itemCls : "x-menu-item",
36517     /**
36518      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36519      */
36520     canActivate : true,
36521     /**
36522      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36523      */
36524     showDelay: 200,
36525     // doc'd in BaseItem
36526     hideDelay: 200,
36527
36528     // private
36529     ctype: "Roo.menu.Item",
36530     
36531     // private
36532     onRender : function(container, position){
36533         var el = document.createElement("a");
36534         el.hideFocus = true;
36535         el.unselectable = "on";
36536         el.href = this.href || "#";
36537         if(this.hrefTarget){
36538             el.target = this.hrefTarget;
36539         }
36540         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36541         
36542         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36543         
36544         el.innerHTML = String.format(
36545                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36546                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36547         this.el = el;
36548         Roo.menu.Item.superclass.onRender.call(this, container, position);
36549     },
36550
36551     /**
36552      * Sets the text to display in this menu item
36553      * @param {String} text The text to display
36554      * @param {Boolean} isHTML true to indicate text is pure html.
36555      */
36556     setText : function(text, isHTML){
36557         if (isHTML) {
36558             this.html = text;
36559         } else {
36560             this.text = text;
36561             this.html = '';
36562         }
36563         if(this.rendered){
36564             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36565      
36566             this.el.update(String.format(
36567                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36568                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36569             this.parentMenu.autoWidth();
36570         }
36571     },
36572
36573     // private
36574     handleClick : function(e){
36575         if(!this.href){ // if no link defined, stop the event automatically
36576             e.stopEvent();
36577         }
36578         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36579     },
36580
36581     // private
36582     activate : function(autoExpand){
36583         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36584             this.focus();
36585             if(autoExpand){
36586                 this.expandMenu();
36587             }
36588         }
36589         return true;
36590     },
36591
36592     // private
36593     shouldDeactivate : function(e){
36594         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36595             if(this.menu && this.menu.isVisible()){
36596                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36597             }
36598             return true;
36599         }
36600         return false;
36601     },
36602
36603     // private
36604     deactivate : function(){
36605         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36606         this.hideMenu();
36607     },
36608
36609     // private
36610     expandMenu : function(autoActivate){
36611         if(!this.disabled && this.menu){
36612             clearTimeout(this.hideTimer);
36613             delete this.hideTimer;
36614             if(!this.menu.isVisible() && !this.showTimer){
36615                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36616             }else if (this.menu.isVisible() && autoActivate){
36617                 this.menu.tryActivate(0, 1);
36618             }
36619         }
36620     },
36621
36622     // private
36623     deferExpand : function(autoActivate){
36624         delete this.showTimer;
36625         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36626         if(autoActivate){
36627             this.menu.tryActivate(0, 1);
36628         }
36629     },
36630
36631     // private
36632     hideMenu : function(){
36633         clearTimeout(this.showTimer);
36634         delete this.showTimer;
36635         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36636             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36637         }
36638     },
36639
36640     // private
36641     deferHide : function(){
36642         delete this.hideTimer;
36643         this.menu.hide();
36644     }
36645 });/*
36646  * Based on:
36647  * Ext JS Library 1.1.1
36648  * Copyright(c) 2006-2007, Ext JS, LLC.
36649  *
36650  * Originally Released Under LGPL - original licence link has changed is not relivant.
36651  *
36652  * Fork - LGPL
36653  * <script type="text/javascript">
36654  */
36655  
36656 /**
36657  * @class Roo.menu.CheckItem
36658  * @extends Roo.menu.Item
36659  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36660  * @constructor
36661  * Creates a new CheckItem
36662  * @param {Object} config Configuration options
36663  */
36664 Roo.menu.CheckItem = function(config){
36665     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36666     this.addEvents({
36667         /**
36668          * @event beforecheckchange
36669          * Fires before the checked value is set, providing an opportunity to cancel if needed
36670          * @param {Roo.menu.CheckItem} this
36671          * @param {Boolean} checked The new checked value that will be set
36672          */
36673         "beforecheckchange" : true,
36674         /**
36675          * @event checkchange
36676          * Fires after the checked value has been set
36677          * @param {Roo.menu.CheckItem} this
36678          * @param {Boolean} checked The checked value that was set
36679          */
36680         "checkchange" : true
36681     });
36682     if(this.checkHandler){
36683         this.on('checkchange', this.checkHandler, this.scope);
36684     }
36685 };
36686 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36687     /**
36688      * @cfg {String} group
36689      * All check items with the same group name will automatically be grouped into a single-select
36690      * radio button group (defaults to '')
36691      */
36692     /**
36693      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36694      */
36695     itemCls : "x-menu-item x-menu-check-item",
36696     /**
36697      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36698      */
36699     groupClass : "x-menu-group-item",
36700
36701     /**
36702      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36703      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36704      * initialized with checked = true will be rendered as checked.
36705      */
36706     checked: false,
36707
36708     // private
36709     ctype: "Roo.menu.CheckItem",
36710
36711     // private
36712     onRender : function(c){
36713         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36714         if(this.group){
36715             this.el.addClass(this.groupClass);
36716         }
36717         Roo.menu.MenuMgr.registerCheckable(this);
36718         if(this.checked){
36719             this.checked = false;
36720             this.setChecked(true, true);
36721         }
36722     },
36723
36724     // private
36725     destroy : function(){
36726         if(this.rendered){
36727             Roo.menu.MenuMgr.unregisterCheckable(this);
36728         }
36729         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36730     },
36731
36732     /**
36733      * Set the checked state of this item
36734      * @param {Boolean} checked The new checked value
36735      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36736      */
36737     setChecked : function(state, suppressEvent){
36738         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36739             if(this.container){
36740                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36741             }
36742             this.checked = state;
36743             if(suppressEvent !== true){
36744                 this.fireEvent("checkchange", this, state);
36745             }
36746         }
36747     },
36748
36749     // private
36750     handleClick : function(e){
36751        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36752            this.setChecked(!this.checked);
36753        }
36754        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36755     }
36756 });/*
36757  * Based on:
36758  * Ext JS Library 1.1.1
36759  * Copyright(c) 2006-2007, Ext JS, LLC.
36760  *
36761  * Originally Released Under LGPL - original licence link has changed is not relivant.
36762  *
36763  * Fork - LGPL
36764  * <script type="text/javascript">
36765  */
36766  
36767 /**
36768  * @class Roo.menu.DateItem
36769  * @extends Roo.menu.Adapter
36770  * A menu item that wraps the {@link Roo.DatPicker} component.
36771  * @constructor
36772  * Creates a new DateItem
36773  * @param {Object} config Configuration options
36774  */
36775 Roo.menu.DateItem = function(config){
36776     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36777     /** The Roo.DatePicker object @type Roo.DatePicker */
36778     this.picker = this.component;
36779     this.addEvents({select: true});
36780     
36781     this.picker.on("render", function(picker){
36782         picker.getEl().swallowEvent("click");
36783         picker.container.addClass("x-menu-date-item");
36784     });
36785
36786     this.picker.on("select", this.onSelect, this);
36787 };
36788
36789 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36790     // private
36791     onSelect : function(picker, date){
36792         this.fireEvent("select", this, date, picker);
36793         Roo.menu.DateItem.superclass.handleClick.call(this);
36794     }
36795 });/*
36796  * Based on:
36797  * Ext JS Library 1.1.1
36798  * Copyright(c) 2006-2007, Ext JS, LLC.
36799  *
36800  * Originally Released Under LGPL - original licence link has changed is not relivant.
36801  *
36802  * Fork - LGPL
36803  * <script type="text/javascript">
36804  */
36805  
36806 /**
36807  * @class Roo.menu.ColorItem
36808  * @extends Roo.menu.Adapter
36809  * A menu item that wraps the {@link Roo.ColorPalette} component.
36810  * @constructor
36811  * Creates a new ColorItem
36812  * @param {Object} config Configuration options
36813  */
36814 Roo.menu.ColorItem = function(config){
36815     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36816     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36817     this.palette = this.component;
36818     this.relayEvents(this.palette, ["select"]);
36819     if(this.selectHandler){
36820         this.on('select', this.selectHandler, this.scope);
36821     }
36822 };
36823 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36824  * Based on:
36825  * Ext JS Library 1.1.1
36826  * Copyright(c) 2006-2007, Ext JS, LLC.
36827  *
36828  * Originally Released Under LGPL - original licence link has changed is not relivant.
36829  *
36830  * Fork - LGPL
36831  * <script type="text/javascript">
36832  */
36833  
36834
36835 /**
36836  * @class Roo.menu.DateMenu
36837  * @extends Roo.menu.Menu
36838  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36839  * @constructor
36840  * Creates a new DateMenu
36841  * @param {Object} config Configuration options
36842  */
36843 Roo.menu.DateMenu = function(config){
36844     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36845     this.plain = true;
36846     var di = new Roo.menu.DateItem(config);
36847     this.add(di);
36848     /**
36849      * The {@link Roo.DatePicker} instance for this DateMenu
36850      * @type DatePicker
36851      */
36852     this.picker = di.picker;
36853     /**
36854      * @event select
36855      * @param {DatePicker} picker
36856      * @param {Date} date
36857      */
36858     this.relayEvents(di, ["select"]);
36859     this.on('beforeshow', function(){
36860         if(this.picker){
36861             this.picker.hideMonthPicker(false);
36862         }
36863     }, this);
36864 };
36865 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36866     cls:'x-date-menu'
36867 });/*
36868  * Based on:
36869  * Ext JS Library 1.1.1
36870  * Copyright(c) 2006-2007, Ext JS, LLC.
36871  *
36872  * Originally Released Under LGPL - original licence link has changed is not relivant.
36873  *
36874  * Fork - LGPL
36875  * <script type="text/javascript">
36876  */
36877  
36878
36879 /**
36880  * @class Roo.menu.ColorMenu
36881  * @extends Roo.menu.Menu
36882  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36883  * @constructor
36884  * Creates a new ColorMenu
36885  * @param {Object} config Configuration options
36886  */
36887 Roo.menu.ColorMenu = function(config){
36888     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36889     this.plain = true;
36890     var ci = new Roo.menu.ColorItem(config);
36891     this.add(ci);
36892     /**
36893      * The {@link Roo.ColorPalette} instance for this ColorMenu
36894      * @type ColorPalette
36895      */
36896     this.palette = ci.palette;
36897     /**
36898      * @event select
36899      * @param {ColorPalette} palette
36900      * @param {String} color
36901      */
36902     this.relayEvents(ci, ["select"]);
36903 };
36904 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36905  * Based on:
36906  * Ext JS Library 1.1.1
36907  * Copyright(c) 2006-2007, Ext JS, LLC.
36908  *
36909  * Originally Released Under LGPL - original licence link has changed is not relivant.
36910  *
36911  * Fork - LGPL
36912  * <script type="text/javascript">
36913  */
36914  
36915 /**
36916  * @class Roo.form.Field
36917  * @extends Roo.BoxComponent
36918  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36919  * @constructor
36920  * Creates a new Field
36921  * @param {Object} config Configuration options
36922  */
36923 Roo.form.Field = function(config){
36924     Roo.form.Field.superclass.constructor.call(this, config);
36925 };
36926
36927 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36928     /**
36929      * @cfg {String} fieldLabel Label to use when rendering a form.
36930      */
36931        /**
36932      * @cfg {String} qtip Mouse over tip
36933      */
36934      
36935     /**
36936      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36937      */
36938     invalidClass : "x-form-invalid",
36939     /**
36940      * @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")
36941      */
36942     invalidText : "The value in this field is invalid",
36943     /**
36944      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36945      */
36946     focusClass : "x-form-focus",
36947     /**
36948      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36949       automatic validation (defaults to "keyup").
36950      */
36951     validationEvent : "keyup",
36952     /**
36953      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36954      */
36955     validateOnBlur : true,
36956     /**
36957      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36958      */
36959     validationDelay : 250,
36960     /**
36961      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36962      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36963      */
36964     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36965     /**
36966      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36967      */
36968     fieldClass : "x-form-field",
36969     /**
36970      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36971      *<pre>
36972 Value         Description
36973 -----------   ----------------------------------------------------------------------
36974 qtip          Display a quick tip when the user hovers over the field
36975 title         Display a default browser title attribute popup
36976 under         Add a block div beneath the field containing the error text
36977 side          Add an error icon to the right of the field with a popup on hover
36978 [element id]  Add the error text directly to the innerHTML of the specified element
36979 </pre>
36980      */
36981     msgTarget : 'qtip',
36982     /**
36983      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36984      */
36985     msgFx : 'normal',
36986
36987     /**
36988      * @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.
36989      */
36990     readOnly : false,
36991
36992     /**
36993      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36994      */
36995     disabled : false,
36996
36997     /**
36998      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36999      */
37000     inputType : undefined,
37001     
37002     /**
37003      * @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).
37004          */
37005         tabIndex : undefined,
37006         
37007     // private
37008     isFormField : true,
37009
37010     // private
37011     hasFocus : false,
37012     /**
37013      * @property {Roo.Element} fieldEl
37014      * Element Containing the rendered Field (with label etc.)
37015      */
37016     /**
37017      * @cfg {Mixed} value A value to initialize this field with.
37018      */
37019     value : undefined,
37020
37021     /**
37022      * @cfg {String} name The field's HTML name attribute.
37023      */
37024     /**
37025      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37026      */
37027
37028         // private ??
37029         initComponent : function(){
37030         Roo.form.Field.superclass.initComponent.call(this);
37031         this.addEvents({
37032             /**
37033              * @event focus
37034              * Fires when this field receives input focus.
37035              * @param {Roo.form.Field} this
37036              */
37037             focus : true,
37038             /**
37039              * @event blur
37040              * Fires when this field loses input focus.
37041              * @param {Roo.form.Field} this
37042              */
37043             blur : true,
37044             /**
37045              * @event specialkey
37046              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37047              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37048              * @param {Roo.form.Field} this
37049              * @param {Roo.EventObject} e The event object
37050              */
37051             specialkey : true,
37052             /**
37053              * @event change
37054              * Fires just before the field blurs if the field value has changed.
37055              * @param {Roo.form.Field} this
37056              * @param {Mixed} newValue The new value
37057              * @param {Mixed} oldValue The original value
37058              */
37059             change : true,
37060             /**
37061              * @event invalid
37062              * Fires after the field has been marked as invalid.
37063              * @param {Roo.form.Field} this
37064              * @param {String} msg The validation message
37065              */
37066             invalid : true,
37067             /**
37068              * @event valid
37069              * Fires after the field has been validated with no errors.
37070              * @param {Roo.form.Field} this
37071              */
37072             valid : true,
37073              /**
37074              * @event keyup
37075              * Fires after the key up
37076              * @param {Roo.form.Field} this
37077              * @param {Roo.EventObject}  e The event Object
37078              */
37079             keyup : true
37080         });
37081     },
37082
37083     /**
37084      * Returns the name attribute of the field if available
37085      * @return {String} name The field name
37086      */
37087     getName: function(){
37088          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37089     },
37090
37091     // private
37092     onRender : function(ct, position){
37093         Roo.form.Field.superclass.onRender.call(this, ct, position);
37094         if(!this.el){
37095             var cfg = this.getAutoCreate();
37096             if(!cfg.name){
37097                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37098             }
37099             if (!cfg.name.length) {
37100                 delete cfg.name;
37101             }
37102             if(this.inputType){
37103                 cfg.type = this.inputType;
37104             }
37105             this.el = ct.createChild(cfg, position);
37106         }
37107         var type = this.el.dom.type;
37108         if(type){
37109             if(type == 'password'){
37110                 type = 'text';
37111             }
37112             this.el.addClass('x-form-'+type);
37113         }
37114         if(this.readOnly){
37115             this.el.dom.readOnly = true;
37116         }
37117         if(this.tabIndex !== undefined){
37118             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37119         }
37120
37121         this.el.addClass([this.fieldClass, this.cls]);
37122         this.initValue();
37123     },
37124
37125     /**
37126      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37127      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37128      * @return {Roo.form.Field} this
37129      */
37130     applyTo : function(target){
37131         this.allowDomMove = false;
37132         this.el = Roo.get(target);
37133         this.render(this.el.dom.parentNode);
37134         return this;
37135     },
37136
37137     // private
37138     initValue : function(){
37139         if(this.value !== undefined){
37140             this.setValue(this.value);
37141         }else if(this.el.dom.value.length > 0){
37142             this.setValue(this.el.dom.value);
37143         }
37144     },
37145
37146     /**
37147      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37148      */
37149     isDirty : function() {
37150         if(this.disabled) {
37151             return false;
37152         }
37153         return String(this.getValue()) !== String(this.originalValue);
37154     },
37155
37156     // private
37157     afterRender : function(){
37158         Roo.form.Field.superclass.afterRender.call(this);
37159         this.initEvents();
37160     },
37161
37162     // private
37163     fireKey : function(e){
37164         //Roo.log('field ' + e.getKey());
37165         if(e.isNavKeyPress()){
37166             this.fireEvent("specialkey", this, e);
37167         }
37168     },
37169
37170     /**
37171      * Resets the current field value to the originally loaded value and clears any validation messages
37172      */
37173     reset : function(){
37174         this.setValue(this.resetValue);
37175         this.clearInvalid();
37176     },
37177
37178     // private
37179     initEvents : function(){
37180         // safari killled keypress - so keydown is now used..
37181         this.el.on("keydown" , this.fireKey,  this);
37182         this.el.on("focus", this.onFocus,  this);
37183         this.el.on("blur", this.onBlur,  this);
37184         this.el.relayEvent('keyup', this);
37185
37186         // reference to original value for reset
37187         this.originalValue = this.getValue();
37188         this.resetValue =  this.getValue();
37189     },
37190
37191     // private
37192     onFocus : function(){
37193         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37194             this.el.addClass(this.focusClass);
37195         }
37196         if(!this.hasFocus){
37197             this.hasFocus = true;
37198             this.startValue = this.getValue();
37199             this.fireEvent("focus", this);
37200         }
37201     },
37202
37203     beforeBlur : Roo.emptyFn,
37204
37205     // private
37206     onBlur : function(){
37207         this.beforeBlur();
37208         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37209             this.el.removeClass(this.focusClass);
37210         }
37211         this.hasFocus = false;
37212         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37213             this.validate();
37214         }
37215         var v = this.getValue();
37216         if(String(v) !== String(this.startValue)){
37217             this.fireEvent('change', this, v, this.startValue);
37218         }
37219         this.fireEvent("blur", this);
37220     },
37221
37222     /**
37223      * Returns whether or not the field value is currently valid
37224      * @param {Boolean} preventMark True to disable marking the field invalid
37225      * @return {Boolean} True if the value is valid, else false
37226      */
37227     isValid : function(preventMark){
37228         if(this.disabled){
37229             return true;
37230         }
37231         var restore = this.preventMark;
37232         this.preventMark = preventMark === true;
37233         var v = this.validateValue(this.processValue(this.getRawValue()));
37234         this.preventMark = restore;
37235         return v;
37236     },
37237
37238     /**
37239      * Validates the field value
37240      * @return {Boolean} True if the value is valid, else false
37241      */
37242     validate : function(){
37243         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37244             this.clearInvalid();
37245             return true;
37246         }
37247         return false;
37248     },
37249
37250     processValue : function(value){
37251         return value;
37252     },
37253
37254     // private
37255     // Subclasses should provide the validation implementation by overriding this
37256     validateValue : function(value){
37257         return true;
37258     },
37259
37260     /**
37261      * Mark this field as invalid
37262      * @param {String} msg The validation message
37263      */
37264     markInvalid : function(msg){
37265         if(!this.rendered || this.preventMark){ // not rendered
37266             return;
37267         }
37268         
37269         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37270         
37271         obj.el.addClass(this.invalidClass);
37272         msg = msg || this.invalidText;
37273         switch(this.msgTarget){
37274             case 'qtip':
37275                 obj.el.dom.qtip = msg;
37276                 obj.el.dom.qclass = 'x-form-invalid-tip';
37277                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37278                     Roo.QuickTips.enable();
37279                 }
37280                 break;
37281             case 'title':
37282                 this.el.dom.title = msg;
37283                 break;
37284             case 'under':
37285                 if(!this.errorEl){
37286                     var elp = this.el.findParent('.x-form-element', 5, true);
37287                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37288                     this.errorEl.setWidth(elp.getWidth(true)-20);
37289                 }
37290                 this.errorEl.update(msg);
37291                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37292                 break;
37293             case 'side':
37294                 if(!this.errorIcon){
37295                     var elp = this.el.findParent('.x-form-element', 5, true);
37296                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37297                 }
37298                 this.alignErrorIcon();
37299                 this.errorIcon.dom.qtip = msg;
37300                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37301                 this.errorIcon.show();
37302                 this.on('resize', this.alignErrorIcon, this);
37303                 break;
37304             default:
37305                 var t = Roo.getDom(this.msgTarget);
37306                 t.innerHTML = msg;
37307                 t.style.display = this.msgDisplay;
37308                 break;
37309         }
37310         this.fireEvent('invalid', this, msg);
37311     },
37312
37313     // private
37314     alignErrorIcon : function(){
37315         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37316     },
37317
37318     /**
37319      * Clear any invalid styles/messages for this field
37320      */
37321     clearInvalid : function(){
37322         if(!this.rendered || this.preventMark){ // not rendered
37323             return;
37324         }
37325         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37326         
37327         obj.el.removeClass(this.invalidClass);
37328         switch(this.msgTarget){
37329             case 'qtip':
37330                 obj.el.dom.qtip = '';
37331                 break;
37332             case 'title':
37333                 this.el.dom.title = '';
37334                 break;
37335             case 'under':
37336                 if(this.errorEl){
37337                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37338                 }
37339                 break;
37340             case 'side':
37341                 if(this.errorIcon){
37342                     this.errorIcon.dom.qtip = '';
37343                     this.errorIcon.hide();
37344                     this.un('resize', this.alignErrorIcon, this);
37345                 }
37346                 break;
37347             default:
37348                 var t = Roo.getDom(this.msgTarget);
37349                 t.innerHTML = '';
37350                 t.style.display = 'none';
37351                 break;
37352         }
37353         this.fireEvent('valid', this);
37354     },
37355
37356     /**
37357      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37358      * @return {Mixed} value The field value
37359      */
37360     getRawValue : function(){
37361         var v = this.el.getValue();
37362         
37363         return v;
37364     },
37365
37366     /**
37367      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37368      * @return {Mixed} value The field value
37369      */
37370     getValue : function(){
37371         var v = this.el.getValue();
37372          
37373         return v;
37374     },
37375
37376     /**
37377      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37378      * @param {Mixed} value The value to set
37379      */
37380     setRawValue : function(v){
37381         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37382     },
37383
37384     /**
37385      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37386      * @param {Mixed} value The value to set
37387      */
37388     setValue : function(v){
37389         this.value = v;
37390         if(this.rendered){
37391             this.el.dom.value = (v === null || v === undefined ? '' : v);
37392              this.validate();
37393         }
37394     },
37395
37396     adjustSize : function(w, h){
37397         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37398         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37399         return s;
37400     },
37401
37402     adjustWidth : function(tag, w){
37403         tag = tag.toLowerCase();
37404         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37405             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37406                 if(tag == 'input'){
37407                     return w + 2;
37408                 }
37409                 if(tag == 'textarea'){
37410                     return w-2;
37411                 }
37412             }else if(Roo.isOpera){
37413                 if(tag == 'input'){
37414                     return w + 2;
37415                 }
37416                 if(tag == 'textarea'){
37417                     return w-2;
37418                 }
37419             }
37420         }
37421         return w;
37422     }
37423 });
37424
37425
37426 // anything other than normal should be considered experimental
37427 Roo.form.Field.msgFx = {
37428     normal : {
37429         show: function(msgEl, f){
37430             msgEl.setDisplayed('block');
37431         },
37432
37433         hide : function(msgEl, f){
37434             msgEl.setDisplayed(false).update('');
37435         }
37436     },
37437
37438     slide : {
37439         show: function(msgEl, f){
37440             msgEl.slideIn('t', {stopFx:true});
37441         },
37442
37443         hide : function(msgEl, f){
37444             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37445         }
37446     },
37447
37448     slideRight : {
37449         show: function(msgEl, f){
37450             msgEl.fixDisplay();
37451             msgEl.alignTo(f.el, 'tl-tr');
37452             msgEl.slideIn('l', {stopFx:true});
37453         },
37454
37455         hide : function(msgEl, f){
37456             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37457         }
37458     }
37459 };/*
37460  * Based on:
37461  * Ext JS Library 1.1.1
37462  * Copyright(c) 2006-2007, Ext JS, LLC.
37463  *
37464  * Originally Released Under LGPL - original licence link has changed is not relivant.
37465  *
37466  * Fork - LGPL
37467  * <script type="text/javascript">
37468  */
37469  
37470
37471 /**
37472  * @class Roo.form.TextField
37473  * @extends Roo.form.Field
37474  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37475  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37476  * @constructor
37477  * Creates a new TextField
37478  * @param {Object} config Configuration options
37479  */
37480 Roo.form.TextField = function(config){
37481     Roo.form.TextField.superclass.constructor.call(this, config);
37482     this.addEvents({
37483         /**
37484          * @event autosize
37485          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37486          * according to the default logic, but this event provides a hook for the developer to apply additional
37487          * logic at runtime to resize the field if needed.
37488              * @param {Roo.form.Field} this This text field
37489              * @param {Number} width The new field width
37490              */
37491         autosize : true
37492     });
37493 };
37494
37495 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37496     /**
37497      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37498      */
37499     grow : false,
37500     /**
37501      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37502      */
37503     growMin : 30,
37504     /**
37505      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37506      */
37507     growMax : 800,
37508     /**
37509      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37510      */
37511     vtype : null,
37512     /**
37513      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37514      */
37515     maskRe : null,
37516     /**
37517      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37518      */
37519     disableKeyFilter : false,
37520     /**
37521      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37522      */
37523     allowBlank : true,
37524     /**
37525      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37526      */
37527     minLength : 0,
37528     /**
37529      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37530      */
37531     maxLength : Number.MAX_VALUE,
37532     /**
37533      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37534      */
37535     minLengthText : "The minimum length for this field is {0}",
37536     /**
37537      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37538      */
37539     maxLengthText : "The maximum length for this field is {0}",
37540     /**
37541      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37542      */
37543     selectOnFocus : false,
37544     /**
37545      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37546      */
37547     blankText : "This field is required",
37548     /**
37549      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37550      * If available, this function will be called only after the basic validators all return true, and will be passed the
37551      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37552      */
37553     validator : null,
37554     /**
37555      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37556      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37557      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37558      */
37559     regex : null,
37560     /**
37561      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37562      */
37563     regexText : "",
37564     /**
37565      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37566      */
37567     emptyText : null,
37568    
37569
37570     // private
37571     initEvents : function()
37572     {
37573         if (this.emptyText) {
37574             this.el.attr('placeholder', this.emptyText);
37575         }
37576         
37577         Roo.form.TextField.superclass.initEvents.call(this);
37578         if(this.validationEvent == 'keyup'){
37579             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37580             this.el.on('keyup', this.filterValidation, this);
37581         }
37582         else if(this.validationEvent !== false){
37583             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37584         }
37585         
37586         if(this.selectOnFocus){
37587             this.on("focus", this.preFocus, this);
37588             
37589         }
37590         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37591             this.el.on("keypress", this.filterKeys, this);
37592         }
37593         if(this.grow){
37594             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37595             this.el.on("click", this.autoSize,  this);
37596         }
37597         if(this.el.is('input[type=password]') && Roo.isSafari){
37598             this.el.on('keydown', this.SafariOnKeyDown, this);
37599         }
37600     },
37601
37602     processValue : function(value){
37603         if(this.stripCharsRe){
37604             var newValue = value.replace(this.stripCharsRe, '');
37605             if(newValue !== value){
37606                 this.setRawValue(newValue);
37607                 return newValue;
37608             }
37609         }
37610         return value;
37611     },
37612
37613     filterValidation : function(e){
37614         if(!e.isNavKeyPress()){
37615             this.validationTask.delay(this.validationDelay);
37616         }
37617     },
37618
37619     // private
37620     onKeyUp : function(e){
37621         if(!e.isNavKeyPress()){
37622             this.autoSize();
37623         }
37624     },
37625
37626     /**
37627      * Resets the current field value to the originally-loaded value and clears any validation messages.
37628      *  
37629      */
37630     reset : function(){
37631         Roo.form.TextField.superclass.reset.call(this);
37632        
37633     },
37634
37635     
37636     // private
37637     preFocus : function(){
37638         
37639         if(this.selectOnFocus){
37640             this.el.dom.select();
37641         }
37642     },
37643
37644     
37645     // private
37646     filterKeys : function(e){
37647         var k = e.getKey();
37648         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37649             return;
37650         }
37651         var c = e.getCharCode(), cc = String.fromCharCode(c);
37652         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37653             return;
37654         }
37655         if(!this.maskRe.test(cc)){
37656             e.stopEvent();
37657         }
37658     },
37659
37660     setValue : function(v){
37661         
37662         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37663         
37664         this.autoSize();
37665     },
37666
37667     /**
37668      * Validates a value according to the field's validation rules and marks the field as invalid
37669      * if the validation fails
37670      * @param {Mixed} value The value to validate
37671      * @return {Boolean} True if the value is valid, else false
37672      */
37673     validateValue : function(value){
37674         if(value.length < 1)  { // if it's blank
37675              if(this.allowBlank){
37676                 this.clearInvalid();
37677                 return true;
37678              }else{
37679                 this.markInvalid(this.blankText);
37680                 return false;
37681              }
37682         }
37683         if(value.length < this.minLength){
37684             this.markInvalid(String.format(this.minLengthText, this.minLength));
37685             return false;
37686         }
37687         if(value.length > this.maxLength){
37688             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37689             return false;
37690         }
37691         if(this.vtype){
37692             var vt = Roo.form.VTypes;
37693             if(!vt[this.vtype](value, this)){
37694                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37695                 return false;
37696             }
37697         }
37698         if(typeof this.validator == "function"){
37699             var msg = this.validator(value);
37700             if(msg !== true){
37701                 this.markInvalid(msg);
37702                 return false;
37703             }
37704         }
37705         if(this.regex && !this.regex.test(value)){
37706             this.markInvalid(this.regexText);
37707             return false;
37708         }
37709         return true;
37710     },
37711
37712     /**
37713      * Selects text in this field
37714      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37715      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37716      */
37717     selectText : function(start, end){
37718         var v = this.getRawValue();
37719         if(v.length > 0){
37720             start = start === undefined ? 0 : start;
37721             end = end === undefined ? v.length : end;
37722             var d = this.el.dom;
37723             if(d.setSelectionRange){
37724                 d.setSelectionRange(start, end);
37725             }else if(d.createTextRange){
37726                 var range = d.createTextRange();
37727                 range.moveStart("character", start);
37728                 range.moveEnd("character", v.length-end);
37729                 range.select();
37730             }
37731         }
37732     },
37733
37734     /**
37735      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37736      * This only takes effect if grow = true, and fires the autosize event.
37737      */
37738     autoSize : function(){
37739         if(!this.grow || !this.rendered){
37740             return;
37741         }
37742         if(!this.metrics){
37743             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37744         }
37745         var el = this.el;
37746         var v = el.dom.value;
37747         var d = document.createElement('div');
37748         d.appendChild(document.createTextNode(v));
37749         v = d.innerHTML;
37750         d = null;
37751         v += "&#160;";
37752         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37753         this.el.setWidth(w);
37754         this.fireEvent("autosize", this, w);
37755     },
37756     
37757     // private
37758     SafariOnKeyDown : function(event)
37759     {
37760         // this is a workaround for a password hang bug on chrome/ webkit.
37761         
37762         var isSelectAll = false;
37763         
37764         if(this.el.dom.selectionEnd > 0){
37765             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37766         }
37767         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37768             event.preventDefault();
37769             this.setValue('');
37770             return;
37771         }
37772         
37773         if(isSelectAll){ // backspace and delete key
37774             
37775             event.preventDefault();
37776             // this is very hacky as keydown always get's upper case.
37777             //
37778             var cc = String.fromCharCode(event.getCharCode());
37779             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37780             
37781         }
37782         
37783         
37784     }
37785 });/*
37786  * Based on:
37787  * Ext JS Library 1.1.1
37788  * Copyright(c) 2006-2007, Ext JS, LLC.
37789  *
37790  * Originally Released Under LGPL - original licence link has changed is not relivant.
37791  *
37792  * Fork - LGPL
37793  * <script type="text/javascript">
37794  */
37795  
37796 /**
37797  * @class Roo.form.Hidden
37798  * @extends Roo.form.TextField
37799  * Simple Hidden element used on forms 
37800  * 
37801  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37802  * 
37803  * @constructor
37804  * Creates a new Hidden form element.
37805  * @param {Object} config Configuration options
37806  */
37807
37808
37809
37810 // easy hidden field...
37811 Roo.form.Hidden = function(config){
37812     Roo.form.Hidden.superclass.constructor.call(this, config);
37813 };
37814   
37815 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37816     fieldLabel:      '',
37817     inputType:      'hidden',
37818     width:          50,
37819     allowBlank:     true,
37820     labelSeparator: '',
37821     hidden:         true,
37822     itemCls :       'x-form-item-display-none'
37823
37824
37825 });
37826
37827
37828 /*
37829  * Based on:
37830  * Ext JS Library 1.1.1
37831  * Copyright(c) 2006-2007, Ext JS, LLC.
37832  *
37833  * Originally Released Under LGPL - original licence link has changed is not relivant.
37834  *
37835  * Fork - LGPL
37836  * <script type="text/javascript">
37837  */
37838  
37839 /**
37840  * @class Roo.form.TriggerField
37841  * @extends Roo.form.TextField
37842  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37843  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37844  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37845  * for which you can provide a custom implementation.  For example:
37846  * <pre><code>
37847 var trigger = new Roo.form.TriggerField();
37848 trigger.onTriggerClick = myTriggerFn;
37849 trigger.applyTo('my-field');
37850 </code></pre>
37851  *
37852  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37853  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37854  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37855  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37856  * @constructor
37857  * Create a new TriggerField.
37858  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37859  * to the base TextField)
37860  */
37861 Roo.form.TriggerField = function(config){
37862     this.mimicing = false;
37863     Roo.form.TriggerField.superclass.constructor.call(this, config);
37864 };
37865
37866 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37867     /**
37868      * @cfg {String} triggerClass A CSS class to apply to the trigger
37869      */
37870     /**
37871      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37872      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37873      */
37874     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37875     /**
37876      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37877      */
37878     hideTrigger:false,
37879
37880     /** @cfg {Boolean} grow @hide */
37881     /** @cfg {Number} growMin @hide */
37882     /** @cfg {Number} growMax @hide */
37883
37884     /**
37885      * @hide 
37886      * @method
37887      */
37888     autoSize: Roo.emptyFn,
37889     // private
37890     monitorTab : true,
37891     // private
37892     deferHeight : true,
37893
37894     
37895     actionMode : 'wrap',
37896     // private
37897     onResize : function(w, h){
37898         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37899         if(typeof w == 'number'){
37900             var x = w - this.trigger.getWidth();
37901             this.el.setWidth(this.adjustWidth('input', x));
37902             this.trigger.setStyle('left', x+'px');
37903         }
37904     },
37905
37906     // private
37907     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37908
37909     // private
37910     getResizeEl : function(){
37911         return this.wrap;
37912     },
37913
37914     // private
37915     getPositionEl : function(){
37916         return this.wrap;
37917     },
37918
37919     // private
37920     alignErrorIcon : function(){
37921         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37922     },
37923
37924     // private
37925     onRender : function(ct, position){
37926         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37927         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37928         this.trigger = this.wrap.createChild(this.triggerConfig ||
37929                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37930         if(this.hideTrigger){
37931             this.trigger.setDisplayed(false);
37932         }
37933         this.initTrigger();
37934         if(!this.width){
37935             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37936         }
37937     },
37938
37939     // private
37940     initTrigger : function(){
37941         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37942         this.trigger.addClassOnOver('x-form-trigger-over');
37943         this.trigger.addClassOnClick('x-form-trigger-click');
37944     },
37945
37946     // private
37947     onDestroy : function(){
37948         if(this.trigger){
37949             this.trigger.removeAllListeners();
37950             this.trigger.remove();
37951         }
37952         if(this.wrap){
37953             this.wrap.remove();
37954         }
37955         Roo.form.TriggerField.superclass.onDestroy.call(this);
37956     },
37957
37958     // private
37959     onFocus : function(){
37960         Roo.form.TriggerField.superclass.onFocus.call(this);
37961         if(!this.mimicing){
37962             this.wrap.addClass('x-trigger-wrap-focus');
37963             this.mimicing = true;
37964             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37965             if(this.monitorTab){
37966                 this.el.on("keydown", this.checkTab, this);
37967             }
37968         }
37969     },
37970
37971     // private
37972     checkTab : function(e){
37973         if(e.getKey() == e.TAB){
37974             this.triggerBlur();
37975         }
37976     },
37977
37978     // private
37979     onBlur : function(){
37980         // do nothing
37981     },
37982
37983     // private
37984     mimicBlur : function(e, t){
37985         if(!this.wrap.contains(t) && this.validateBlur()){
37986             this.triggerBlur();
37987         }
37988     },
37989
37990     // private
37991     triggerBlur : function(){
37992         this.mimicing = false;
37993         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37994         if(this.monitorTab){
37995             this.el.un("keydown", this.checkTab, this);
37996         }
37997         this.wrap.removeClass('x-trigger-wrap-focus');
37998         Roo.form.TriggerField.superclass.onBlur.call(this);
37999     },
38000
38001     // private
38002     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38003     validateBlur : function(e, t){
38004         return true;
38005     },
38006
38007     // private
38008     onDisable : function(){
38009         Roo.form.TriggerField.superclass.onDisable.call(this);
38010         if(this.wrap){
38011             this.wrap.addClass('x-item-disabled');
38012         }
38013     },
38014
38015     // private
38016     onEnable : function(){
38017         Roo.form.TriggerField.superclass.onEnable.call(this);
38018         if(this.wrap){
38019             this.wrap.removeClass('x-item-disabled');
38020         }
38021     },
38022
38023     // private
38024     onShow : function(){
38025         var ae = this.getActionEl();
38026         
38027         if(ae){
38028             ae.dom.style.display = '';
38029             ae.dom.style.visibility = 'visible';
38030         }
38031     },
38032
38033     // private
38034     
38035     onHide : function(){
38036         var ae = this.getActionEl();
38037         ae.dom.style.display = 'none';
38038     },
38039
38040     /**
38041      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38042      * by an implementing function.
38043      * @method
38044      * @param {EventObject} e
38045      */
38046     onTriggerClick : Roo.emptyFn
38047 });
38048
38049 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38050 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38051 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38052 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38053     initComponent : function(){
38054         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38055
38056         this.triggerConfig = {
38057             tag:'span', cls:'x-form-twin-triggers', cn:[
38058             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38059             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38060         ]};
38061     },
38062
38063     getTrigger : function(index){
38064         return this.triggers[index];
38065     },
38066
38067     initTrigger : function(){
38068         var ts = this.trigger.select('.x-form-trigger', true);
38069         this.wrap.setStyle('overflow', 'hidden');
38070         var triggerField = this;
38071         ts.each(function(t, all, index){
38072             t.hide = function(){
38073                 var w = triggerField.wrap.getWidth();
38074                 this.dom.style.display = 'none';
38075                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38076             };
38077             t.show = function(){
38078                 var w = triggerField.wrap.getWidth();
38079                 this.dom.style.display = '';
38080                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38081             };
38082             var triggerIndex = 'Trigger'+(index+1);
38083
38084             if(this['hide'+triggerIndex]){
38085                 t.dom.style.display = 'none';
38086             }
38087             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38088             t.addClassOnOver('x-form-trigger-over');
38089             t.addClassOnClick('x-form-trigger-click');
38090         }, this);
38091         this.triggers = ts.elements;
38092     },
38093
38094     onTrigger1Click : Roo.emptyFn,
38095     onTrigger2Click : Roo.emptyFn
38096 });/*
38097  * Based on:
38098  * Ext JS Library 1.1.1
38099  * Copyright(c) 2006-2007, Ext JS, LLC.
38100  *
38101  * Originally Released Under LGPL - original licence link has changed is not relivant.
38102  *
38103  * Fork - LGPL
38104  * <script type="text/javascript">
38105  */
38106  
38107 /**
38108  * @class Roo.form.TextArea
38109  * @extends Roo.form.TextField
38110  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38111  * support for auto-sizing.
38112  * @constructor
38113  * Creates a new TextArea
38114  * @param {Object} config Configuration options
38115  */
38116 Roo.form.TextArea = function(config){
38117     Roo.form.TextArea.superclass.constructor.call(this, config);
38118     // these are provided exchanges for backwards compat
38119     // minHeight/maxHeight were replaced by growMin/growMax to be
38120     // compatible with TextField growing config values
38121     if(this.minHeight !== undefined){
38122         this.growMin = this.minHeight;
38123     }
38124     if(this.maxHeight !== undefined){
38125         this.growMax = this.maxHeight;
38126     }
38127 };
38128
38129 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38130     /**
38131      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38132      */
38133     growMin : 60,
38134     /**
38135      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38136      */
38137     growMax: 1000,
38138     /**
38139      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38140      * in the field (equivalent to setting overflow: hidden, defaults to false)
38141      */
38142     preventScrollbars: false,
38143     /**
38144      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38145      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38146      */
38147
38148     // private
38149     onRender : function(ct, position){
38150         if(!this.el){
38151             this.defaultAutoCreate = {
38152                 tag: "textarea",
38153                 style:"width:300px;height:60px;",
38154                 autocomplete: "off"
38155             };
38156         }
38157         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38158         if(this.grow){
38159             this.textSizeEl = Roo.DomHelper.append(document.body, {
38160                 tag: "pre", cls: "x-form-grow-sizer"
38161             });
38162             if(this.preventScrollbars){
38163                 this.el.setStyle("overflow", "hidden");
38164             }
38165             this.el.setHeight(this.growMin);
38166         }
38167     },
38168
38169     onDestroy : function(){
38170         if(this.textSizeEl){
38171             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38172         }
38173         Roo.form.TextArea.superclass.onDestroy.call(this);
38174     },
38175
38176     // private
38177     onKeyUp : function(e){
38178         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38179             this.autoSize();
38180         }
38181     },
38182
38183     /**
38184      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38185      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38186      */
38187     autoSize : function(){
38188         if(!this.grow || !this.textSizeEl){
38189             return;
38190         }
38191         var el = this.el;
38192         var v = el.dom.value;
38193         var ts = this.textSizeEl;
38194
38195         ts.innerHTML = '';
38196         ts.appendChild(document.createTextNode(v));
38197         v = ts.innerHTML;
38198
38199         Roo.fly(ts).setWidth(this.el.getWidth());
38200         if(v.length < 1){
38201             v = "&#160;&#160;";
38202         }else{
38203             if(Roo.isIE){
38204                 v = v.replace(/\n/g, '<p>&#160;</p>');
38205             }
38206             v += "&#160;\n&#160;";
38207         }
38208         ts.innerHTML = v;
38209         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38210         if(h != this.lastHeight){
38211             this.lastHeight = h;
38212             this.el.setHeight(h);
38213             this.fireEvent("autosize", this, h);
38214         }
38215     }
38216 });/*
38217  * Based on:
38218  * Ext JS Library 1.1.1
38219  * Copyright(c) 2006-2007, Ext JS, LLC.
38220  *
38221  * Originally Released Under LGPL - original licence link has changed is not relivant.
38222  *
38223  * Fork - LGPL
38224  * <script type="text/javascript">
38225  */
38226  
38227
38228 /**
38229  * @class Roo.form.NumberField
38230  * @extends Roo.form.TextField
38231  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38232  * @constructor
38233  * Creates a new NumberField
38234  * @param {Object} config Configuration options
38235  */
38236 Roo.form.NumberField = function(config){
38237     Roo.form.NumberField.superclass.constructor.call(this, config);
38238 };
38239
38240 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38241     /**
38242      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38243      */
38244     fieldClass: "x-form-field x-form-num-field",
38245     /**
38246      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38247      */
38248     allowDecimals : true,
38249     /**
38250      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38251      */
38252     decimalSeparator : ".",
38253     /**
38254      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38255      */
38256     decimalPrecision : 2,
38257     /**
38258      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38259      */
38260     allowNegative : true,
38261     /**
38262      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38263      */
38264     minValue : Number.NEGATIVE_INFINITY,
38265     /**
38266      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38267      */
38268     maxValue : Number.MAX_VALUE,
38269     /**
38270      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38271      */
38272     minText : "The minimum value for this field is {0}",
38273     /**
38274      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38275      */
38276     maxText : "The maximum value for this field is {0}",
38277     /**
38278      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38279      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38280      */
38281     nanText : "{0} is not a valid number",
38282
38283     // private
38284     initEvents : function(){
38285         Roo.form.NumberField.superclass.initEvents.call(this);
38286         var allowed = "0123456789";
38287         if(this.allowDecimals){
38288             allowed += this.decimalSeparator;
38289         }
38290         if(this.allowNegative){
38291             allowed += "-";
38292         }
38293         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38294         var keyPress = function(e){
38295             var k = e.getKey();
38296             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38297                 return;
38298             }
38299             var c = e.getCharCode();
38300             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38301                 e.stopEvent();
38302             }
38303         };
38304         this.el.on("keypress", keyPress, this);
38305     },
38306
38307     // private
38308     validateValue : function(value){
38309         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38310             return false;
38311         }
38312         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38313              return true;
38314         }
38315         var num = this.parseValue(value);
38316         if(isNaN(num)){
38317             this.markInvalid(String.format(this.nanText, value));
38318             return false;
38319         }
38320         if(num < this.minValue){
38321             this.markInvalid(String.format(this.minText, this.minValue));
38322             return false;
38323         }
38324         if(num > this.maxValue){
38325             this.markInvalid(String.format(this.maxText, this.maxValue));
38326             return false;
38327         }
38328         return true;
38329     },
38330
38331     getValue : function(){
38332         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38333     },
38334
38335     // private
38336     parseValue : function(value){
38337         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38338         return isNaN(value) ? '' : value;
38339     },
38340
38341     // private
38342     fixPrecision : function(value){
38343         var nan = isNaN(value);
38344         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38345             return nan ? '' : value;
38346         }
38347         return parseFloat(value).toFixed(this.decimalPrecision);
38348     },
38349
38350     setValue : function(v){
38351         v = this.fixPrecision(v);
38352         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38353     },
38354
38355     // private
38356     decimalPrecisionFcn : function(v){
38357         return Math.floor(v);
38358     },
38359
38360     beforeBlur : function(){
38361         var v = this.parseValue(this.getRawValue());
38362         if(v){
38363             this.setValue(v);
38364         }
38365     }
38366 });/*
38367  * Based on:
38368  * Ext JS Library 1.1.1
38369  * Copyright(c) 2006-2007, Ext JS, LLC.
38370  *
38371  * Originally Released Under LGPL - original licence link has changed is not relivant.
38372  *
38373  * Fork - LGPL
38374  * <script type="text/javascript">
38375  */
38376  
38377 /**
38378  * @class Roo.form.DateField
38379  * @extends Roo.form.TriggerField
38380  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38381 * @constructor
38382 * Create a new DateField
38383 * @param {Object} config
38384  */
38385 Roo.form.DateField = function(config){
38386     Roo.form.DateField.superclass.constructor.call(this, config);
38387     
38388       this.addEvents({
38389          
38390         /**
38391          * @event select
38392          * Fires when a date is selected
38393              * @param {Roo.form.DateField} combo This combo box
38394              * @param {Date} date The date selected
38395              */
38396         'select' : true
38397          
38398     });
38399     
38400     
38401     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38402     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38403     this.ddMatch = null;
38404     if(this.disabledDates){
38405         var dd = this.disabledDates;
38406         var re = "(?:";
38407         for(var i = 0; i < dd.length; i++){
38408             re += dd[i];
38409             if(i != dd.length-1) re += "|";
38410         }
38411         this.ddMatch = new RegExp(re + ")");
38412     }
38413 };
38414
38415 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38416     /**
38417      * @cfg {String} format
38418      * The default date format string which can be overriden for localization support.  The format must be
38419      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38420      */
38421     format : "m/d/y",
38422     /**
38423      * @cfg {String} altFormats
38424      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38425      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38426      */
38427     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38428     /**
38429      * @cfg {Array} disabledDays
38430      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38431      */
38432     disabledDays : null,
38433     /**
38434      * @cfg {String} disabledDaysText
38435      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38436      */
38437     disabledDaysText : "Disabled",
38438     /**
38439      * @cfg {Array} disabledDates
38440      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38441      * expression so they are very powerful. Some examples:
38442      * <ul>
38443      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38444      * <li>["03/08", "09/16"] would disable those days for every year</li>
38445      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38446      * <li>["03/../2006"] would disable every day in March 2006</li>
38447      * <li>["^03"] would disable every day in every March</li>
38448      * </ul>
38449      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38450      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38451      */
38452     disabledDates : null,
38453     /**
38454      * @cfg {String} disabledDatesText
38455      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38456      */
38457     disabledDatesText : "Disabled",
38458     /**
38459      * @cfg {Date/String} minValue
38460      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38461      * valid format (defaults to null).
38462      */
38463     minValue : null,
38464     /**
38465      * @cfg {Date/String} maxValue
38466      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38467      * valid format (defaults to null).
38468      */
38469     maxValue : null,
38470     /**
38471      * @cfg {String} minText
38472      * The error text to display when the date in the cell is before minValue (defaults to
38473      * 'The date in this field must be after {minValue}').
38474      */
38475     minText : "The date in this field must be equal to or after {0}",
38476     /**
38477      * @cfg {String} maxText
38478      * The error text to display when the date in the cell is after maxValue (defaults to
38479      * 'The date in this field must be before {maxValue}').
38480      */
38481     maxText : "The date in this field must be equal to or before {0}",
38482     /**
38483      * @cfg {String} invalidText
38484      * The error text to display when the date in the field is invalid (defaults to
38485      * '{value} is not a valid date - it must be in the format {format}').
38486      */
38487     invalidText : "{0} is not a valid date - it must be in the format {1}",
38488     /**
38489      * @cfg {String} triggerClass
38490      * An additional CSS class used to style the trigger button.  The trigger will always get the
38491      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38492      * which displays a calendar icon).
38493      */
38494     triggerClass : 'x-form-date-trigger',
38495     
38496
38497     /**
38498      * @cfg {Boolean} useIso
38499      * if enabled, then the date field will use a hidden field to store the 
38500      * real value as iso formated date. default (false)
38501      */ 
38502     useIso : false,
38503     /**
38504      * @cfg {String/Object} autoCreate
38505      * A DomHelper element spec, or true for a default element spec (defaults to
38506      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38507      */ 
38508     // private
38509     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38510     
38511     // private
38512     hiddenField: false,
38513     
38514     onRender : function(ct, position)
38515     {
38516         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38517         if (this.useIso) {
38518             //this.el.dom.removeAttribute('name'); 
38519             Roo.log("Changing name?");
38520             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38521             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38522                     'before', true);
38523             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38524             // prevent input submission
38525             this.hiddenName = this.name;
38526         }
38527             
38528             
38529     },
38530     
38531     // private
38532     validateValue : function(value)
38533     {
38534         value = this.formatDate(value);
38535         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38536             Roo.log('super failed');
38537             return false;
38538         }
38539         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38540              return true;
38541         }
38542         var svalue = value;
38543         value = this.parseDate(value);
38544         if(!value){
38545             Roo.log('parse date failed' + svalue);
38546             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38547             return false;
38548         }
38549         var time = value.getTime();
38550         if(this.minValue && time < this.minValue.getTime()){
38551             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38552             return false;
38553         }
38554         if(this.maxValue && time > this.maxValue.getTime()){
38555             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38556             return false;
38557         }
38558         if(this.disabledDays){
38559             var day = value.getDay();
38560             for(var i = 0; i < this.disabledDays.length; i++) {
38561                 if(day === this.disabledDays[i]){
38562                     this.markInvalid(this.disabledDaysText);
38563                     return false;
38564                 }
38565             }
38566         }
38567         var fvalue = this.formatDate(value);
38568         if(this.ddMatch && this.ddMatch.test(fvalue)){
38569             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38570             return false;
38571         }
38572         return true;
38573     },
38574
38575     // private
38576     // Provides logic to override the default TriggerField.validateBlur which just returns true
38577     validateBlur : function(){
38578         return !this.menu || !this.menu.isVisible();
38579     },
38580     
38581     getName: function()
38582     {
38583         // returns hidden if it's set..
38584         if (!this.rendered) {return ''};
38585         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38586         
38587     },
38588
38589     /**
38590      * Returns the current date value of the date field.
38591      * @return {Date} The date value
38592      */
38593     getValue : function(){
38594         
38595         return  this.hiddenField ?
38596                 this.hiddenField.value :
38597                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38598     },
38599
38600     /**
38601      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38602      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38603      * (the default format used is "m/d/y").
38604      * <br />Usage:
38605      * <pre><code>
38606 //All of these calls set the same date value (May 4, 2006)
38607
38608 //Pass a date object:
38609 var dt = new Date('5/4/06');
38610 dateField.setValue(dt);
38611
38612 //Pass a date string (default format):
38613 dateField.setValue('5/4/06');
38614
38615 //Pass a date string (custom format):
38616 dateField.format = 'Y-m-d';
38617 dateField.setValue('2006-5-4');
38618 </code></pre>
38619      * @param {String/Date} date The date or valid date string
38620      */
38621     setValue : function(date){
38622         if (this.hiddenField) {
38623             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38624         }
38625         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38626         // make sure the value field is always stored as a date..
38627         this.value = this.parseDate(date);
38628         
38629         
38630     },
38631
38632     // private
38633     parseDate : function(value){
38634         if(!value || value instanceof Date){
38635             return value;
38636         }
38637         var v = Date.parseDate(value, this.format);
38638          if (!v && this.useIso) {
38639             v = Date.parseDate(value, 'Y-m-d');
38640         }
38641         if(!v && this.altFormats){
38642             if(!this.altFormatsArray){
38643                 this.altFormatsArray = this.altFormats.split("|");
38644             }
38645             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38646                 v = Date.parseDate(value, this.altFormatsArray[i]);
38647             }
38648         }
38649         return v;
38650     },
38651
38652     // private
38653     formatDate : function(date, fmt){
38654         return (!date || !(date instanceof Date)) ?
38655                date : date.dateFormat(fmt || this.format);
38656     },
38657
38658     // private
38659     menuListeners : {
38660         select: function(m, d){
38661             
38662             this.setValue(d);
38663             this.fireEvent('select', this, d);
38664         },
38665         show : function(){ // retain focus styling
38666             this.onFocus();
38667         },
38668         hide : function(){
38669             this.focus.defer(10, this);
38670             var ml = this.menuListeners;
38671             this.menu.un("select", ml.select,  this);
38672             this.menu.un("show", ml.show,  this);
38673             this.menu.un("hide", ml.hide,  this);
38674         }
38675     },
38676
38677     // private
38678     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38679     onTriggerClick : function(){
38680         if(this.disabled){
38681             return;
38682         }
38683         if(this.menu == null){
38684             this.menu = new Roo.menu.DateMenu();
38685         }
38686         Roo.apply(this.menu.picker,  {
38687             showClear: this.allowBlank,
38688             minDate : this.minValue,
38689             maxDate : this.maxValue,
38690             disabledDatesRE : this.ddMatch,
38691             disabledDatesText : this.disabledDatesText,
38692             disabledDays : this.disabledDays,
38693             disabledDaysText : this.disabledDaysText,
38694             format : this.useIso ? 'Y-m-d' : this.format,
38695             minText : String.format(this.minText, this.formatDate(this.minValue)),
38696             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38697         });
38698         this.menu.on(Roo.apply({}, this.menuListeners, {
38699             scope:this
38700         }));
38701         this.menu.picker.setValue(this.getValue() || new Date());
38702         this.menu.show(this.el, "tl-bl?");
38703     },
38704
38705     beforeBlur : function(){
38706         var v = this.parseDate(this.getRawValue());
38707         if(v){
38708             this.setValue(v);
38709         }
38710     },
38711
38712     /*@
38713      * overide
38714      * 
38715      */
38716     isDirty : function() {
38717         if(this.disabled) {
38718             return false;
38719         }
38720         
38721         if(typeof(this.startValue) === 'undefined'){
38722             return false;
38723         }
38724         
38725         return String(this.getValue()) !== String(this.startValue);
38726         
38727     }
38728 });/*
38729  * Based on:
38730  * Ext JS Library 1.1.1
38731  * Copyright(c) 2006-2007, Ext JS, LLC.
38732  *
38733  * Originally Released Under LGPL - original licence link has changed is not relivant.
38734  *
38735  * Fork - LGPL
38736  * <script type="text/javascript">
38737  */
38738  
38739 /**
38740  * @class Roo.form.MonthField
38741  * @extends Roo.form.TriggerField
38742  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38743 * @constructor
38744 * Create a new MonthField
38745 * @param {Object} config
38746  */
38747 Roo.form.MonthField = function(config){
38748     
38749     Roo.form.MonthField.superclass.constructor.call(this, config);
38750     
38751       this.addEvents({
38752          
38753         /**
38754          * @event select
38755          * Fires when a date is selected
38756              * @param {Roo.form.MonthFieeld} combo This combo box
38757              * @param {Date} date The date selected
38758              */
38759         'select' : true
38760          
38761     });
38762     
38763     
38764     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38765     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38766     this.ddMatch = null;
38767     if(this.disabledDates){
38768         var dd = this.disabledDates;
38769         var re = "(?:";
38770         for(var i = 0; i < dd.length; i++){
38771             re += dd[i];
38772             if(i != dd.length-1) re += "|";
38773         }
38774         this.ddMatch = new RegExp(re + ")");
38775     }
38776 };
38777
38778 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38779     /**
38780      * @cfg {String} format
38781      * The default date format string which can be overriden for localization support.  The format must be
38782      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38783      */
38784     format : "M Y",
38785     /**
38786      * @cfg {String} altFormats
38787      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38788      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38789      */
38790     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38791     /**
38792      * @cfg {Array} disabledDays
38793      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38794      */
38795     disabledDays : [0,1,2,3,4,5,6],
38796     /**
38797      * @cfg {String} disabledDaysText
38798      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38799      */
38800     disabledDaysText : "Disabled",
38801     /**
38802      * @cfg {Array} disabledDates
38803      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38804      * expression so they are very powerful. Some examples:
38805      * <ul>
38806      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38807      * <li>["03/08", "09/16"] would disable those days for every year</li>
38808      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38809      * <li>["03/../2006"] would disable every day in March 2006</li>
38810      * <li>["^03"] would disable every day in every March</li>
38811      * </ul>
38812      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38813      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38814      */
38815     disabledDates : null,
38816     /**
38817      * @cfg {String} disabledDatesText
38818      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38819      */
38820     disabledDatesText : "Disabled",
38821     /**
38822      * @cfg {Date/String} minValue
38823      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38824      * valid format (defaults to null).
38825      */
38826     minValue : null,
38827     /**
38828      * @cfg {Date/String} maxValue
38829      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38830      * valid format (defaults to null).
38831      */
38832     maxValue : null,
38833     /**
38834      * @cfg {String} minText
38835      * The error text to display when the date in the cell is before minValue (defaults to
38836      * 'The date in this field must be after {minValue}').
38837      */
38838     minText : "The date in this field must be equal to or after {0}",
38839     /**
38840      * @cfg {String} maxTextf
38841      * The error text to display when the date in the cell is after maxValue (defaults to
38842      * 'The date in this field must be before {maxValue}').
38843      */
38844     maxText : "The date in this field must be equal to or before {0}",
38845     /**
38846      * @cfg {String} invalidText
38847      * The error text to display when the date in the field is invalid (defaults to
38848      * '{value} is not a valid date - it must be in the format {format}').
38849      */
38850     invalidText : "{0} is not a valid date - it must be in the format {1}",
38851     /**
38852      * @cfg {String} triggerClass
38853      * An additional CSS class used to style the trigger button.  The trigger will always get the
38854      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38855      * which displays a calendar icon).
38856      */
38857     triggerClass : 'x-form-date-trigger',
38858     
38859
38860     /**
38861      * @cfg {Boolean} useIso
38862      * if enabled, then the date field will use a hidden field to store the 
38863      * real value as iso formated date. default (true)
38864      */ 
38865     useIso : true,
38866     /**
38867      * @cfg {String/Object} autoCreate
38868      * A DomHelper element spec, or true for a default element spec (defaults to
38869      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38870      */ 
38871     // private
38872     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38873     
38874     // private
38875     hiddenField: false,
38876     
38877     hideMonthPicker : false,
38878     
38879     onRender : function(ct, position)
38880     {
38881         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38882         if (this.useIso) {
38883             this.el.dom.removeAttribute('name'); 
38884             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38885                     'before', true);
38886             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38887             // prevent input submission
38888             this.hiddenName = this.name;
38889         }
38890             
38891             
38892     },
38893     
38894     // private
38895     validateValue : function(value)
38896     {
38897         value = this.formatDate(value);
38898         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38899             return false;
38900         }
38901         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38902              return true;
38903         }
38904         var svalue = value;
38905         value = this.parseDate(value);
38906         if(!value){
38907             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38908             return false;
38909         }
38910         var time = value.getTime();
38911         if(this.minValue && time < this.minValue.getTime()){
38912             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38913             return false;
38914         }
38915         if(this.maxValue && time > this.maxValue.getTime()){
38916             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38917             return false;
38918         }
38919         /*if(this.disabledDays){
38920             var day = value.getDay();
38921             for(var i = 0; i < this.disabledDays.length; i++) {
38922                 if(day === this.disabledDays[i]){
38923                     this.markInvalid(this.disabledDaysText);
38924                     return false;
38925                 }
38926             }
38927         }
38928         */
38929         var fvalue = this.formatDate(value);
38930         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38931             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38932             return false;
38933         }
38934         */
38935         return true;
38936     },
38937
38938     // private
38939     // Provides logic to override the default TriggerField.validateBlur which just returns true
38940     validateBlur : function(){
38941         return !this.menu || !this.menu.isVisible();
38942     },
38943
38944     /**
38945      * Returns the current date value of the date field.
38946      * @return {Date} The date value
38947      */
38948     getValue : function(){
38949         
38950         
38951         
38952         return  this.hiddenField ?
38953                 this.hiddenField.value :
38954                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38955     },
38956
38957     /**
38958      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38959      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38960      * (the default format used is "m/d/y").
38961      * <br />Usage:
38962      * <pre><code>
38963 //All of these calls set the same date value (May 4, 2006)
38964
38965 //Pass a date object:
38966 var dt = new Date('5/4/06');
38967 monthField.setValue(dt);
38968
38969 //Pass a date string (default format):
38970 monthField.setValue('5/4/06');
38971
38972 //Pass a date string (custom format):
38973 monthField.format = 'Y-m-d';
38974 monthField.setValue('2006-5-4');
38975 </code></pre>
38976      * @param {String/Date} date The date or valid date string
38977      */
38978     setValue : function(date){
38979         Roo.log('month setValue' + date);
38980         // can only be first of month..
38981         
38982         var val = this.parseDate(date);
38983         
38984         if (this.hiddenField) {
38985             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38986         }
38987         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38988         this.value = this.parseDate(date);
38989     },
38990
38991     // private
38992     parseDate : function(value){
38993         if(!value || value instanceof Date){
38994             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38995             return value;
38996         }
38997         var v = Date.parseDate(value, this.format);
38998         if (!v && this.useIso) {
38999             v = Date.parseDate(value, 'Y-m-d');
39000         }
39001         if (v) {
39002             // 
39003             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39004         }
39005         
39006         
39007         if(!v && this.altFormats){
39008             if(!this.altFormatsArray){
39009                 this.altFormatsArray = this.altFormats.split("|");
39010             }
39011             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39012                 v = Date.parseDate(value, this.altFormatsArray[i]);
39013             }
39014         }
39015         return v;
39016     },
39017
39018     // private
39019     formatDate : function(date, fmt){
39020         return (!date || !(date instanceof Date)) ?
39021                date : date.dateFormat(fmt || this.format);
39022     },
39023
39024     // private
39025     menuListeners : {
39026         select: function(m, d){
39027             this.setValue(d);
39028             this.fireEvent('select', this, d);
39029         },
39030         show : function(){ // retain focus styling
39031             this.onFocus();
39032         },
39033         hide : function(){
39034             this.focus.defer(10, this);
39035             var ml = this.menuListeners;
39036             this.menu.un("select", ml.select,  this);
39037             this.menu.un("show", ml.show,  this);
39038             this.menu.un("hide", ml.hide,  this);
39039         }
39040     },
39041     // private
39042     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39043     onTriggerClick : function(){
39044         if(this.disabled){
39045             return;
39046         }
39047         if(this.menu == null){
39048             this.menu = new Roo.menu.DateMenu();
39049            
39050         }
39051         
39052         Roo.apply(this.menu.picker,  {
39053             
39054             showClear: this.allowBlank,
39055             minDate : this.minValue,
39056             maxDate : this.maxValue,
39057             disabledDatesRE : this.ddMatch,
39058             disabledDatesText : this.disabledDatesText,
39059             
39060             format : this.useIso ? 'Y-m-d' : this.format,
39061             minText : String.format(this.minText, this.formatDate(this.minValue)),
39062             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39063             
39064         });
39065          this.menu.on(Roo.apply({}, this.menuListeners, {
39066             scope:this
39067         }));
39068        
39069         
39070         var m = this.menu;
39071         var p = m.picker;
39072         
39073         // hide month picker get's called when we called by 'before hide';
39074         
39075         var ignorehide = true;
39076         p.hideMonthPicker  = function(disableAnim){
39077             if (ignorehide) {
39078                 return;
39079             }
39080              if(this.monthPicker){
39081                 Roo.log("hideMonthPicker called");
39082                 if(disableAnim === true){
39083                     this.monthPicker.hide();
39084                 }else{
39085                     this.monthPicker.slideOut('t', {duration:.2});
39086                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39087                     p.fireEvent("select", this, this.value);
39088                     m.hide();
39089                 }
39090             }
39091         }
39092         
39093         Roo.log('picker set value');
39094         Roo.log(this.getValue());
39095         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39096         m.show(this.el, 'tl-bl?');
39097         ignorehide  = false;
39098         // this will trigger hideMonthPicker..
39099         
39100         
39101         // hidden the day picker
39102         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39103         
39104         
39105         
39106       
39107         
39108         p.showMonthPicker.defer(100, p);
39109     
39110         
39111        
39112     },
39113
39114     beforeBlur : function(){
39115         var v = this.parseDate(this.getRawValue());
39116         if(v){
39117             this.setValue(v);
39118         }
39119     }
39120
39121     /** @cfg {Boolean} grow @hide */
39122     /** @cfg {Number} growMin @hide */
39123     /** @cfg {Number} growMax @hide */
39124     /**
39125      * @hide
39126      * @method autoSize
39127      */
39128 });/*
39129  * Based on:
39130  * Ext JS Library 1.1.1
39131  * Copyright(c) 2006-2007, Ext JS, LLC.
39132  *
39133  * Originally Released Under LGPL - original licence link has changed is not relivant.
39134  *
39135  * Fork - LGPL
39136  * <script type="text/javascript">
39137  */
39138  
39139
39140 /**
39141  * @class Roo.form.ComboBox
39142  * @extends Roo.form.TriggerField
39143  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39144  * @constructor
39145  * Create a new ComboBox.
39146  * @param {Object} config Configuration options
39147  */
39148 Roo.form.ComboBox = function(config){
39149     Roo.form.ComboBox.superclass.constructor.call(this, config);
39150     this.addEvents({
39151         /**
39152          * @event expand
39153          * Fires when the dropdown list is expanded
39154              * @param {Roo.form.ComboBox} combo This combo box
39155              */
39156         'expand' : true,
39157         /**
39158          * @event collapse
39159          * Fires when the dropdown list is collapsed
39160              * @param {Roo.form.ComboBox} combo This combo box
39161              */
39162         'collapse' : true,
39163         /**
39164          * @event beforeselect
39165          * Fires before a list item is selected. Return false to cancel the selection.
39166              * @param {Roo.form.ComboBox} combo This combo box
39167              * @param {Roo.data.Record} record The data record returned from the underlying store
39168              * @param {Number} index The index of the selected item in the dropdown list
39169              */
39170         'beforeselect' : true,
39171         /**
39172          * @event select
39173          * Fires when a list item is selected
39174              * @param {Roo.form.ComboBox} combo This combo box
39175              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39176              * @param {Number} index The index of the selected item in the dropdown list
39177              */
39178         'select' : true,
39179         /**
39180          * @event beforequery
39181          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39182          * The event object passed has these properties:
39183              * @param {Roo.form.ComboBox} combo This combo box
39184              * @param {String} query The query
39185              * @param {Boolean} forceAll true to force "all" query
39186              * @param {Boolean} cancel true to cancel the query
39187              * @param {Object} e The query event object
39188              */
39189         'beforequery': true,
39190          /**
39191          * @event add
39192          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39193              * @param {Roo.form.ComboBox} combo This combo box
39194              */
39195         'add' : true,
39196         /**
39197          * @event edit
39198          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39199              * @param {Roo.form.ComboBox} combo This combo box
39200              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39201              */
39202         'edit' : true
39203         
39204         
39205     });
39206     if(this.transform){
39207         this.allowDomMove = false;
39208         var s = Roo.getDom(this.transform);
39209         if(!this.hiddenName){
39210             this.hiddenName = s.name;
39211         }
39212         if(!this.store){
39213             this.mode = 'local';
39214             var d = [], opts = s.options;
39215             for(var i = 0, len = opts.length;i < len; i++){
39216                 var o = opts[i];
39217                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39218                 if(o.selected) {
39219                     this.value = value;
39220                 }
39221                 d.push([value, o.text]);
39222             }
39223             this.store = new Roo.data.SimpleStore({
39224                 'id': 0,
39225                 fields: ['value', 'text'],
39226                 data : d
39227             });
39228             this.valueField = 'value';
39229             this.displayField = 'text';
39230         }
39231         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39232         if(!this.lazyRender){
39233             this.target = true;
39234             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39235             s.parentNode.removeChild(s); // remove it
39236             this.render(this.el.parentNode);
39237         }else{
39238             s.parentNode.removeChild(s); // remove it
39239         }
39240
39241     }
39242     if (this.store) {
39243         this.store = Roo.factory(this.store, Roo.data);
39244     }
39245     
39246     this.selectedIndex = -1;
39247     if(this.mode == 'local'){
39248         if(config.queryDelay === undefined){
39249             this.queryDelay = 10;
39250         }
39251         if(config.minChars === undefined){
39252             this.minChars = 0;
39253         }
39254     }
39255 };
39256
39257 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39258     /**
39259      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39260      */
39261     /**
39262      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39263      * rendering into an Roo.Editor, defaults to false)
39264      */
39265     /**
39266      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39267      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39268      */
39269     /**
39270      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39271      */
39272     /**
39273      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39274      * the dropdown list (defaults to undefined, with no header element)
39275      */
39276
39277      /**
39278      * @cfg {String/Roo.Template} tpl The template to use to render the output
39279      */
39280      
39281     // private
39282     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39283     /**
39284      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39285      */
39286     listWidth: undefined,
39287     /**
39288      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39289      * mode = 'remote' or 'text' if mode = 'local')
39290      */
39291     displayField: undefined,
39292     /**
39293      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39294      * mode = 'remote' or 'value' if mode = 'local'). 
39295      * Note: use of a valueField requires the user make a selection
39296      * in order for a value to be mapped.
39297      */
39298     valueField: undefined,
39299     
39300     
39301     /**
39302      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39303      * field's data value (defaults to the underlying DOM element's name)
39304      */
39305     hiddenName: undefined,
39306     /**
39307      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39308      */
39309     listClass: '',
39310     /**
39311      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39312      */
39313     selectedClass: 'x-combo-selected',
39314     /**
39315      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39316      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39317      * which displays a downward arrow icon).
39318      */
39319     triggerClass : 'x-form-arrow-trigger',
39320     /**
39321      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39322      */
39323     shadow:'sides',
39324     /**
39325      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39326      * anchor positions (defaults to 'tl-bl')
39327      */
39328     listAlign: 'tl-bl?',
39329     /**
39330      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39331      */
39332     maxHeight: 300,
39333     /**
39334      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39335      * query specified by the allQuery config option (defaults to 'query')
39336      */
39337     triggerAction: 'query',
39338     /**
39339      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39340      * (defaults to 4, does not apply if editable = false)
39341      */
39342     minChars : 4,
39343     /**
39344      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39345      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39346      */
39347     typeAhead: false,
39348     /**
39349      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39350      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39351      */
39352     queryDelay: 500,
39353     /**
39354      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39355      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39356      */
39357     pageSize: 0,
39358     /**
39359      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39360      * when editable = true (defaults to false)
39361      */
39362     selectOnFocus:false,
39363     /**
39364      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39365      */
39366     queryParam: 'query',
39367     /**
39368      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39369      * when mode = 'remote' (defaults to 'Loading...')
39370      */
39371     loadingText: 'Loading...',
39372     /**
39373      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39374      */
39375     resizable: false,
39376     /**
39377      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39378      */
39379     handleHeight : 8,
39380     /**
39381      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39382      * traditional select (defaults to true)
39383      */
39384     editable: true,
39385     /**
39386      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39387      */
39388     allQuery: '',
39389     /**
39390      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39391      */
39392     mode: 'remote',
39393     /**
39394      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39395      * listWidth has a higher value)
39396      */
39397     minListWidth : 70,
39398     /**
39399      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39400      * allow the user to set arbitrary text into the field (defaults to false)
39401      */
39402     forceSelection:false,
39403     /**
39404      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39405      * if typeAhead = true (defaults to 250)
39406      */
39407     typeAheadDelay : 250,
39408     /**
39409      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39410      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39411      */
39412     valueNotFoundText : undefined,
39413     /**
39414      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39415      */
39416     blockFocus : false,
39417     
39418     /**
39419      * @cfg {Boolean} disableClear Disable showing of clear button.
39420      */
39421     disableClear : false,
39422     /**
39423      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39424      */
39425     alwaysQuery : false,
39426     
39427     //private
39428     addicon : false,
39429     editicon: false,
39430     
39431     // element that contains real text value.. (when hidden is used..)
39432      
39433     // private
39434     onRender : function(ct, position){
39435         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39436         if(this.hiddenName){
39437             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39438                     'before', true);
39439             this.hiddenField.value =
39440                 this.hiddenValue !== undefined ? this.hiddenValue :
39441                 this.value !== undefined ? this.value : '';
39442
39443             // prevent input submission
39444             this.el.dom.removeAttribute('name');
39445              
39446              
39447         }
39448         if(Roo.isGecko){
39449             this.el.dom.setAttribute('autocomplete', 'off');
39450         }
39451
39452         var cls = 'x-combo-list';
39453
39454         this.list = new Roo.Layer({
39455             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39456         });
39457
39458         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39459         this.list.setWidth(lw);
39460         this.list.swallowEvent('mousewheel');
39461         this.assetHeight = 0;
39462
39463         if(this.title){
39464             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39465             this.assetHeight += this.header.getHeight();
39466         }
39467
39468         this.innerList = this.list.createChild({cls:cls+'-inner'});
39469         this.innerList.on('mouseover', this.onViewOver, this);
39470         this.innerList.on('mousemove', this.onViewMove, this);
39471         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39472         
39473         if(this.allowBlank && !this.pageSize && !this.disableClear){
39474             this.footer = this.list.createChild({cls:cls+'-ft'});
39475             this.pageTb = new Roo.Toolbar(this.footer);
39476            
39477         }
39478         if(this.pageSize){
39479             this.footer = this.list.createChild({cls:cls+'-ft'});
39480             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39481                     {pageSize: this.pageSize});
39482             
39483         }
39484         
39485         if (this.pageTb && this.allowBlank && !this.disableClear) {
39486             var _this = this;
39487             this.pageTb.add(new Roo.Toolbar.Fill(), {
39488                 cls: 'x-btn-icon x-btn-clear',
39489                 text: '&#160;',
39490                 handler: function()
39491                 {
39492                     _this.collapse();
39493                     _this.clearValue();
39494                     _this.onSelect(false, -1);
39495                 }
39496             });
39497         }
39498         if (this.footer) {
39499             this.assetHeight += this.footer.getHeight();
39500         }
39501         
39502
39503         if(!this.tpl){
39504             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39505         }
39506
39507         this.view = new Roo.View(this.innerList, this.tpl, {
39508             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39509         });
39510
39511         this.view.on('click', this.onViewClick, this);
39512
39513         this.store.on('beforeload', this.onBeforeLoad, this);
39514         this.store.on('load', this.onLoad, this);
39515         this.store.on('loadexception', this.onLoadException, this);
39516
39517         if(this.resizable){
39518             this.resizer = new Roo.Resizable(this.list,  {
39519                pinned:true, handles:'se'
39520             });
39521             this.resizer.on('resize', function(r, w, h){
39522                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39523                 this.listWidth = w;
39524                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39525                 this.restrictHeight();
39526             }, this);
39527             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39528         }
39529         if(!this.editable){
39530             this.editable = true;
39531             this.setEditable(false);
39532         }  
39533         
39534         
39535         if (typeof(this.events.add.listeners) != 'undefined') {
39536             
39537             this.addicon = this.wrap.createChild(
39538                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39539        
39540             this.addicon.on('click', function(e) {
39541                 this.fireEvent('add', this);
39542             }, this);
39543         }
39544         if (typeof(this.events.edit.listeners) != 'undefined') {
39545             
39546             this.editicon = this.wrap.createChild(
39547                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39548             if (this.addicon) {
39549                 this.editicon.setStyle('margin-left', '40px');
39550             }
39551             this.editicon.on('click', function(e) {
39552                 
39553                 // we fire even  if inothing is selected..
39554                 this.fireEvent('edit', this, this.lastData );
39555                 
39556             }, this);
39557         }
39558         
39559         
39560         
39561     },
39562
39563     // private
39564     initEvents : function(){
39565         Roo.form.ComboBox.superclass.initEvents.call(this);
39566
39567         this.keyNav = new Roo.KeyNav(this.el, {
39568             "up" : function(e){
39569                 this.inKeyMode = true;
39570                 this.selectPrev();
39571             },
39572
39573             "down" : function(e){
39574                 if(!this.isExpanded()){
39575                     this.onTriggerClick();
39576                 }else{
39577                     this.inKeyMode = true;
39578                     this.selectNext();
39579                 }
39580             },
39581
39582             "enter" : function(e){
39583                 this.onViewClick();
39584                 //return true;
39585             },
39586
39587             "esc" : function(e){
39588                 this.collapse();
39589             },
39590
39591             "tab" : function(e){
39592                 this.onViewClick(false);
39593                 this.fireEvent("specialkey", this, e);
39594                 return true;
39595             },
39596
39597             scope : this,
39598
39599             doRelay : function(foo, bar, hname){
39600                 if(hname == 'down' || this.scope.isExpanded()){
39601                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39602                 }
39603                 return true;
39604             },
39605
39606             forceKeyDown: true
39607         });
39608         this.queryDelay = Math.max(this.queryDelay || 10,
39609                 this.mode == 'local' ? 10 : 250);
39610         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39611         if(this.typeAhead){
39612             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39613         }
39614         if(this.editable !== false){
39615             this.el.on("keyup", this.onKeyUp, this);
39616         }
39617         if(this.forceSelection){
39618             this.on('blur', this.doForce, this);
39619         }
39620     },
39621
39622     onDestroy : function(){
39623         if(this.view){
39624             this.view.setStore(null);
39625             this.view.el.removeAllListeners();
39626             this.view.el.remove();
39627             this.view.purgeListeners();
39628         }
39629         if(this.list){
39630             this.list.destroy();
39631         }
39632         if(this.store){
39633             this.store.un('beforeload', this.onBeforeLoad, this);
39634             this.store.un('load', this.onLoad, this);
39635             this.store.un('loadexception', this.onLoadException, this);
39636         }
39637         Roo.form.ComboBox.superclass.onDestroy.call(this);
39638     },
39639
39640     // private
39641     fireKey : function(e){
39642         if(e.isNavKeyPress() && !this.list.isVisible()){
39643             this.fireEvent("specialkey", this, e);
39644         }
39645     },
39646
39647     // private
39648     onResize: function(w, h){
39649         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39650         
39651         if(typeof w != 'number'){
39652             // we do not handle it!?!?
39653             return;
39654         }
39655         var tw = this.trigger.getWidth();
39656         tw += this.addicon ? this.addicon.getWidth() : 0;
39657         tw += this.editicon ? this.editicon.getWidth() : 0;
39658         var x = w - tw;
39659         this.el.setWidth( this.adjustWidth('input', x));
39660             
39661         this.trigger.setStyle('left', x+'px');
39662         
39663         if(this.list && this.listWidth === undefined){
39664             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39665             this.list.setWidth(lw);
39666             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39667         }
39668         
39669     
39670         
39671     },
39672
39673     /**
39674      * Allow or prevent the user from directly editing the field text.  If false is passed,
39675      * the user will only be able to select from the items defined in the dropdown list.  This method
39676      * is the runtime equivalent of setting the 'editable' config option at config time.
39677      * @param {Boolean} value True to allow the user to directly edit the field text
39678      */
39679     setEditable : function(value){
39680         if(value == this.editable){
39681             return;
39682         }
39683         this.editable = value;
39684         if(!value){
39685             this.el.dom.setAttribute('readOnly', true);
39686             this.el.on('mousedown', this.onTriggerClick,  this);
39687             this.el.addClass('x-combo-noedit');
39688         }else{
39689             this.el.dom.setAttribute('readOnly', false);
39690             this.el.un('mousedown', this.onTriggerClick,  this);
39691             this.el.removeClass('x-combo-noedit');
39692         }
39693     },
39694
39695     // private
39696     onBeforeLoad : function(){
39697         if(!this.hasFocus){
39698             return;
39699         }
39700         this.innerList.update(this.loadingText ?
39701                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39702         this.restrictHeight();
39703         this.selectedIndex = -1;
39704     },
39705
39706     // private
39707     onLoad : function(){
39708         if(!this.hasFocus){
39709             return;
39710         }
39711         if(this.store.getCount() > 0){
39712             this.expand();
39713             this.restrictHeight();
39714             if(this.lastQuery == this.allQuery){
39715                 if(this.editable){
39716                     this.el.dom.select();
39717                 }
39718                 if(!this.selectByValue(this.value, true)){
39719                     this.select(0, true);
39720                 }
39721             }else{
39722                 this.selectNext();
39723                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39724                     this.taTask.delay(this.typeAheadDelay);
39725                 }
39726             }
39727         }else{
39728             this.onEmptyResults();
39729         }
39730         //this.el.focus();
39731     },
39732     // private
39733     onLoadException : function()
39734     {
39735         this.collapse();
39736         Roo.log(this.store.reader.jsonData);
39737         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39738             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39739         }
39740         
39741         
39742     },
39743     // private
39744     onTypeAhead : function(){
39745         if(this.store.getCount() > 0){
39746             var r = this.store.getAt(0);
39747             var newValue = r.data[this.displayField];
39748             var len = newValue.length;
39749             var selStart = this.getRawValue().length;
39750             if(selStart != len){
39751                 this.setRawValue(newValue);
39752                 this.selectText(selStart, newValue.length);
39753             }
39754         }
39755     },
39756
39757     // private
39758     onSelect : function(record, index){
39759         if(this.fireEvent('beforeselect', this, record, index) !== false){
39760             this.setFromData(index > -1 ? record.data : false);
39761             this.collapse();
39762             this.fireEvent('select', this, record, index);
39763         }
39764     },
39765
39766     /**
39767      * Returns the currently selected field value or empty string if no value is set.
39768      * @return {String} value The selected value
39769      */
39770     getValue : function(){
39771         if(this.valueField){
39772             return typeof this.value != 'undefined' ? this.value : '';
39773         }else{
39774             return Roo.form.ComboBox.superclass.getValue.call(this);
39775         }
39776     },
39777
39778     /**
39779      * Clears any text/value currently set in the field
39780      */
39781     clearValue : function(){
39782         if(this.hiddenField){
39783             this.hiddenField.value = '';
39784         }
39785         this.value = '';
39786         this.setRawValue('');
39787         this.lastSelectionText = '';
39788         
39789     },
39790
39791     /**
39792      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39793      * will be displayed in the field.  If the value does not match the data value of an existing item,
39794      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39795      * Otherwise the field will be blank (although the value will still be set).
39796      * @param {String} value The value to match
39797      */
39798     setValue : function(v){
39799         var text = v;
39800         if(this.valueField){
39801             var r = this.findRecord(this.valueField, v);
39802             if(r){
39803                 text = r.data[this.displayField];
39804             }else if(this.valueNotFoundText !== undefined){
39805                 text = this.valueNotFoundText;
39806             }
39807         }
39808         this.lastSelectionText = text;
39809         if(this.hiddenField){
39810             this.hiddenField.value = v;
39811         }
39812         Roo.form.ComboBox.superclass.setValue.call(this, text);
39813         this.value = v;
39814     },
39815     /**
39816      * @property {Object} the last set data for the element
39817      */
39818     
39819     lastData : false,
39820     /**
39821      * Sets the value of the field based on a object which is related to the record format for the store.
39822      * @param {Object} value the value to set as. or false on reset?
39823      */
39824     setFromData : function(o){
39825         var dv = ''; // display value
39826         var vv = ''; // value value..
39827         this.lastData = o;
39828         if (this.displayField) {
39829             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39830         } else {
39831             // this is an error condition!!!
39832             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39833         }
39834         
39835         if(this.valueField){
39836             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39837         }
39838         if(this.hiddenField){
39839             this.hiddenField.value = vv;
39840             
39841             this.lastSelectionText = dv;
39842             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39843             this.value = vv;
39844             return;
39845         }
39846         // no hidden field.. - we store the value in 'value', but still display
39847         // display field!!!!
39848         this.lastSelectionText = dv;
39849         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39850         this.value = vv;
39851         
39852         
39853     },
39854     // private
39855     reset : function(){
39856         // overridden so that last data is reset..
39857         this.setValue(this.resetValue);
39858         this.clearInvalid();
39859         this.lastData = false;
39860         if (this.view) {
39861             this.view.clearSelections();
39862         }
39863     },
39864     // private
39865     findRecord : function(prop, value){
39866         var record;
39867         if(this.store.getCount() > 0){
39868             this.store.each(function(r){
39869                 if(r.data[prop] == value){
39870                     record = r;
39871                     return false;
39872                 }
39873                 return true;
39874             });
39875         }
39876         return record;
39877     },
39878     
39879     getName: function()
39880     {
39881         // returns hidden if it's set..
39882         if (!this.rendered) {return ''};
39883         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39884         
39885     },
39886     // private
39887     onViewMove : function(e, t){
39888         this.inKeyMode = false;
39889     },
39890
39891     // private
39892     onViewOver : function(e, t){
39893         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39894             return;
39895         }
39896         var item = this.view.findItemFromChild(t);
39897         if(item){
39898             var index = this.view.indexOf(item);
39899             this.select(index, false);
39900         }
39901     },
39902
39903     // private
39904     onViewClick : function(doFocus)
39905     {
39906         var index = this.view.getSelectedIndexes()[0];
39907         var r = this.store.getAt(index);
39908         if(r){
39909             this.onSelect(r, index);
39910         }
39911         if(doFocus !== false && !this.blockFocus){
39912             this.el.focus();
39913         }
39914     },
39915
39916     // private
39917     restrictHeight : function(){
39918         this.innerList.dom.style.height = '';
39919         var inner = this.innerList.dom;
39920         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39921         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39922         this.list.beginUpdate();
39923         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39924         this.list.alignTo(this.el, this.listAlign);
39925         this.list.endUpdate();
39926     },
39927
39928     // private
39929     onEmptyResults : function(){
39930         this.collapse();
39931     },
39932
39933     /**
39934      * Returns true if the dropdown list is expanded, else false.
39935      */
39936     isExpanded : function(){
39937         return this.list.isVisible();
39938     },
39939
39940     /**
39941      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39942      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39943      * @param {String} value The data value of the item to select
39944      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39945      * selected item if it is not currently in view (defaults to true)
39946      * @return {Boolean} True if the value matched an item in the list, else false
39947      */
39948     selectByValue : function(v, scrollIntoView){
39949         if(v !== undefined && v !== null){
39950             var r = this.findRecord(this.valueField || this.displayField, v);
39951             if(r){
39952                 this.select(this.store.indexOf(r), scrollIntoView);
39953                 return true;
39954             }
39955         }
39956         return false;
39957     },
39958
39959     /**
39960      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39961      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39962      * @param {Number} index The zero-based index of the list item to select
39963      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39964      * selected item if it is not currently in view (defaults to true)
39965      */
39966     select : function(index, scrollIntoView){
39967         this.selectedIndex = index;
39968         this.view.select(index);
39969         if(scrollIntoView !== false){
39970             var el = this.view.getNode(index);
39971             if(el){
39972                 this.innerList.scrollChildIntoView(el, false);
39973             }
39974         }
39975     },
39976
39977     // private
39978     selectNext : function(){
39979         var ct = this.store.getCount();
39980         if(ct > 0){
39981             if(this.selectedIndex == -1){
39982                 this.select(0);
39983             }else if(this.selectedIndex < ct-1){
39984                 this.select(this.selectedIndex+1);
39985             }
39986         }
39987     },
39988
39989     // private
39990     selectPrev : function(){
39991         var ct = this.store.getCount();
39992         if(ct > 0){
39993             if(this.selectedIndex == -1){
39994                 this.select(0);
39995             }else if(this.selectedIndex != 0){
39996                 this.select(this.selectedIndex-1);
39997             }
39998         }
39999     },
40000
40001     // private
40002     onKeyUp : function(e){
40003         if(this.editable !== false && !e.isSpecialKey()){
40004             this.lastKey = e.getKey();
40005             this.dqTask.delay(this.queryDelay);
40006         }
40007     },
40008
40009     // private
40010     validateBlur : function(){
40011         return !this.list || !this.list.isVisible();   
40012     },
40013
40014     // private
40015     initQuery : function(){
40016         this.doQuery(this.getRawValue());
40017     },
40018
40019     // private
40020     doForce : function(){
40021         if(this.el.dom.value.length > 0){
40022             this.el.dom.value =
40023                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40024              
40025         }
40026     },
40027
40028     /**
40029      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40030      * query allowing the query action to be canceled if needed.
40031      * @param {String} query The SQL query to execute
40032      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40033      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40034      * saved in the current store (defaults to false)
40035      */
40036     doQuery : function(q, forceAll){
40037         if(q === undefined || q === null){
40038             q = '';
40039         }
40040         var qe = {
40041             query: q,
40042             forceAll: forceAll,
40043             combo: this,
40044             cancel:false
40045         };
40046         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40047             return false;
40048         }
40049         q = qe.query;
40050         forceAll = qe.forceAll;
40051         if(forceAll === true || (q.length >= this.minChars)){
40052             if(this.lastQuery != q || this.alwaysQuery){
40053                 this.lastQuery = q;
40054                 if(this.mode == 'local'){
40055                     this.selectedIndex = -1;
40056                     if(forceAll){
40057                         this.store.clearFilter();
40058                     }else{
40059                         this.store.filter(this.displayField, q);
40060                     }
40061                     this.onLoad();
40062                 }else{
40063                     this.store.baseParams[this.queryParam] = q;
40064                     this.store.load({
40065                         params: this.getParams(q)
40066                     });
40067                     this.expand();
40068                 }
40069             }else{
40070                 this.selectedIndex = -1;
40071                 this.onLoad();   
40072             }
40073         }
40074     },
40075
40076     // private
40077     getParams : function(q){
40078         var p = {};
40079         //p[this.queryParam] = q;
40080         if(this.pageSize){
40081             p.start = 0;
40082             p.limit = this.pageSize;
40083         }
40084         return p;
40085     },
40086
40087     /**
40088      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40089      */
40090     collapse : function(){
40091         if(!this.isExpanded()){
40092             return;
40093         }
40094         this.list.hide();
40095         Roo.get(document).un('mousedown', this.collapseIf, this);
40096         Roo.get(document).un('mousewheel', this.collapseIf, this);
40097         if (!this.editable) {
40098             Roo.get(document).un('keydown', this.listKeyPress, this);
40099         }
40100         this.fireEvent('collapse', this);
40101     },
40102
40103     // private
40104     collapseIf : function(e){
40105         if(!e.within(this.wrap) && !e.within(this.list)){
40106             this.collapse();
40107         }
40108     },
40109
40110     /**
40111      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40112      */
40113     expand : function(){
40114         if(this.isExpanded() || !this.hasFocus){
40115             return;
40116         }
40117         this.list.alignTo(this.el, this.listAlign);
40118         this.list.show();
40119         Roo.get(document).on('mousedown', this.collapseIf, this);
40120         Roo.get(document).on('mousewheel', this.collapseIf, this);
40121         if (!this.editable) {
40122             Roo.get(document).on('keydown', this.listKeyPress, this);
40123         }
40124         
40125         this.fireEvent('expand', this);
40126     },
40127
40128     // private
40129     // Implements the default empty TriggerField.onTriggerClick function
40130     onTriggerClick : function(){
40131         if(this.disabled){
40132             return;
40133         }
40134         if(this.isExpanded()){
40135             this.collapse();
40136             if (!this.blockFocus) {
40137                 this.el.focus();
40138             }
40139             
40140         }else {
40141             this.hasFocus = true;
40142             if(this.triggerAction == 'all') {
40143                 this.doQuery(this.allQuery, true);
40144             } else {
40145                 this.doQuery(this.getRawValue());
40146             }
40147             if (!this.blockFocus) {
40148                 this.el.focus();
40149             }
40150         }
40151     },
40152     listKeyPress : function(e)
40153     {
40154         //Roo.log('listkeypress');
40155         // scroll to first matching element based on key pres..
40156         if (e.isSpecialKey()) {
40157             return false;
40158         }
40159         var k = String.fromCharCode(e.getKey()).toUpperCase();
40160         //Roo.log(k);
40161         var match  = false;
40162         var csel = this.view.getSelectedNodes();
40163         var cselitem = false;
40164         if (csel.length) {
40165             var ix = this.view.indexOf(csel[0]);
40166             cselitem  = this.store.getAt(ix);
40167             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40168                 cselitem = false;
40169             }
40170             
40171         }
40172         
40173         this.store.each(function(v) { 
40174             if (cselitem) {
40175                 // start at existing selection.
40176                 if (cselitem.id == v.id) {
40177                     cselitem = false;
40178                 }
40179                 return;
40180             }
40181                 
40182             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40183                 match = this.store.indexOf(v);
40184                 return false;
40185             }
40186         }, this);
40187         
40188         if (match === false) {
40189             return true; // no more action?
40190         }
40191         // scroll to?
40192         this.view.select(match);
40193         var sn = Roo.get(this.view.getSelectedNodes()[0])
40194         sn.scrollIntoView(sn.dom.parentNode, false);
40195     }
40196
40197     /** 
40198     * @cfg {Boolean} grow 
40199     * @hide 
40200     */
40201     /** 
40202     * @cfg {Number} growMin 
40203     * @hide 
40204     */
40205     /** 
40206     * @cfg {Number} growMax 
40207     * @hide 
40208     */
40209     /**
40210      * @hide
40211      * @method autoSize
40212      */
40213 });/*
40214  * Copyright(c) 2010-2012, Roo J Solutions Limited
40215  *
40216  * Licence LGPL
40217  *
40218  */
40219
40220 /**
40221  * @class Roo.form.ComboBoxArray
40222  * @extends Roo.form.TextField
40223  * A facebook style adder... for lists of email / people / countries  etc...
40224  * pick multiple items from a combo box, and shows each one.
40225  *
40226  *  Fred [x]  Brian [x]  [Pick another |v]
40227  *
40228  *
40229  *  For this to work: it needs various extra information
40230  *    - normal combo problay has
40231  *      name, hiddenName
40232  *    + displayField, valueField
40233  *
40234  *    For our purpose...
40235  *
40236  *
40237  *   If we change from 'extends' to wrapping...
40238  *   
40239  *  
40240  *
40241  
40242  
40243  * @constructor
40244  * Create a new ComboBoxArray.
40245  * @param {Object} config Configuration options
40246  */
40247  
40248
40249 Roo.form.ComboBoxArray = function(config)
40250 {
40251     this.addEvents({
40252         /**
40253          * @event remove
40254          * Fires when remove the value from the list
40255              * @param {Roo.form.ComboBoxArray} _self This combo box array
40256              * @param {Roo.form.ComboBoxArray.Item} item removed item
40257              */
40258         'remove' : true
40259         
40260         
40261     });
40262     
40263     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40264     
40265     this.items = new Roo.util.MixedCollection(false);
40266     
40267     // construct the child combo...
40268     
40269     
40270     
40271     
40272    
40273     
40274 }
40275
40276  
40277 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40278
40279     /**
40280      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40281      */
40282     
40283     lastData : false,
40284     
40285     // behavies liek a hiddne field
40286     inputType:      'hidden',
40287     /**
40288      * @cfg {Number} width The width of the box that displays the selected element
40289      */ 
40290     width:          300,
40291
40292     
40293     
40294     /**
40295      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40296      */
40297     name : false,
40298     /**
40299      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40300      */
40301     hiddenName : false,
40302     
40303     
40304     // private the array of items that are displayed..
40305     items  : false,
40306     // private - the hidden field el.
40307     hiddenEl : false,
40308     // private - the filed el..
40309     el : false,
40310     
40311     //validateValue : function() { return true; }, // all values are ok!
40312     //onAddClick: function() { },
40313     
40314     onRender : function(ct, position) 
40315     {
40316         
40317         // create the standard hidden element
40318         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40319         
40320         
40321         // give fake names to child combo;
40322         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40323         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40324         
40325         this.combo = Roo.factory(this.combo, Roo.form);
40326         this.combo.onRender(ct, position);
40327         if (typeof(this.combo.width) != 'undefined') {
40328             this.combo.onResize(this.combo.width,0);
40329         }
40330         
40331         this.combo.initEvents();
40332         
40333         // assigned so form know we need to do this..
40334         this.store          = this.combo.store;
40335         this.valueField     = this.combo.valueField;
40336         this.displayField   = this.combo.displayField ;
40337         
40338         
40339         this.combo.wrap.addClass('x-cbarray-grp');
40340         
40341         var cbwrap = this.combo.wrap.createChild(
40342             {tag: 'div', cls: 'x-cbarray-cb'},
40343             this.combo.el.dom
40344         );
40345         
40346              
40347         this.hiddenEl = this.combo.wrap.createChild({
40348             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40349         });
40350         this.el = this.combo.wrap.createChild({
40351             tag: 'input',  type:'hidden' , name: this.name, value : ''
40352         });
40353          //   this.el.dom.removeAttribute("name");
40354         
40355         
40356         this.outerWrap = this.combo.wrap;
40357         this.wrap = cbwrap;
40358         
40359         this.outerWrap.setWidth(this.width);
40360         this.outerWrap.dom.removeChild(this.el.dom);
40361         
40362         this.wrap.dom.appendChild(this.el.dom);
40363         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40364         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40365         
40366         this.combo.trigger.setStyle('position','relative');
40367         this.combo.trigger.setStyle('left', '0px');
40368         this.combo.trigger.setStyle('top', '2px');
40369         
40370         this.combo.el.setStyle('vertical-align', 'text-bottom');
40371         
40372         //this.trigger.setStyle('vertical-align', 'top');
40373         
40374         // this should use the code from combo really... on('add' ....)
40375         if (this.adder) {
40376             
40377         
40378             this.adder = this.outerWrap.createChild(
40379                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40380             var _t = this;
40381             this.adder.on('click', function(e) {
40382                 _t.fireEvent('adderclick', this, e);
40383             }, _t);
40384         }
40385         //var _t = this;
40386         //this.adder.on('click', this.onAddClick, _t);
40387         
40388         
40389         this.combo.on('select', function(cb, rec, ix) {
40390             this.addItem(rec.data);
40391             
40392             cb.setValue('');
40393             cb.el.dom.value = '';
40394             //cb.lastData = rec.data;
40395             // add to list
40396             
40397         }, this);
40398         
40399         
40400     },
40401     
40402     
40403     getName: function()
40404     {
40405         // returns hidden if it's set..
40406         if (!this.rendered) {return ''};
40407         return  this.hiddenName ? this.hiddenName : this.name;
40408         
40409     },
40410     
40411     
40412     onResize: function(w, h){
40413         
40414         return;
40415         // not sure if this is needed..
40416         //this.combo.onResize(w,h);
40417         
40418         if(typeof w != 'number'){
40419             // we do not handle it!?!?
40420             return;
40421         }
40422         var tw = this.combo.trigger.getWidth();
40423         tw += this.addicon ? this.addicon.getWidth() : 0;
40424         tw += this.editicon ? this.editicon.getWidth() : 0;
40425         var x = w - tw;
40426         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40427             
40428         this.combo.trigger.setStyle('left', '0px');
40429         
40430         if(this.list && this.listWidth === undefined){
40431             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40432             this.list.setWidth(lw);
40433             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40434         }
40435         
40436     
40437         
40438     },
40439     
40440     addItem: function(rec)
40441     {
40442         var valueField = this.combo.valueField;
40443         var displayField = this.combo.displayField;
40444         if (this.items.indexOfKey(rec[valueField]) > -1) {
40445             //console.log("GOT " + rec.data.id);
40446             return;
40447         }
40448         
40449         var x = new Roo.form.ComboBoxArray.Item({
40450             //id : rec[this.idField],
40451             data : rec,
40452             displayField : displayField ,
40453             tipField : displayField ,
40454             cb : this
40455         });
40456         // use the 
40457         this.items.add(rec[valueField],x);
40458         // add it before the element..
40459         this.updateHiddenEl();
40460         x.render(this.outerWrap, this.wrap.dom);
40461         // add the image handler..
40462     },
40463     
40464     updateHiddenEl : function()
40465     {
40466         this.validate();
40467         if (!this.hiddenEl) {
40468             return;
40469         }
40470         var ar = [];
40471         var idField = this.combo.valueField;
40472         
40473         this.items.each(function(f) {
40474             ar.push(f.data[idField]);
40475            
40476         });
40477         this.hiddenEl.dom.value = ar.join(',');
40478         this.validate();
40479     },
40480     
40481     reset : function()
40482     {
40483         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40484         this.items.each(function(f) {
40485            f.remove(); 
40486         });
40487         this.el.dom.value = '';
40488         if (this.hiddenEl) {
40489             this.hiddenEl.dom.value = '';
40490         }
40491         
40492     },
40493     getValue: function()
40494     {
40495         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40496     },
40497     setValue: function(v) // not a valid action - must use addItems..
40498     {
40499          
40500         this.reset();
40501         
40502         
40503         
40504         if (this.store.isLocal && (typeof(v) == 'string')) {
40505             // then we can use the store to find the values..
40506             // comma seperated at present.. this needs to allow JSON based encoding..
40507             this.hiddenEl.value  = v;
40508             var v_ar = [];
40509             Roo.each(v.split(','), function(k) {
40510                 Roo.log("CHECK " + this.valueField + ',' + k);
40511                 var li = this.store.query(this.valueField, k);
40512                 if (!li.length) {
40513                     return;
40514                 }
40515                 var add = {};
40516                 add[this.valueField] = k;
40517                 add[this.displayField] = li.item(0).data[this.displayField];
40518                 
40519                 this.addItem(add);
40520             }, this) 
40521              
40522         }
40523         if (typeof(v) == 'object') {
40524             // then let's assume it's an array of objects..
40525             Roo.each(v, function(l) {
40526                 this.addItem(l);
40527             }, this);
40528              
40529         }
40530         
40531         
40532     },
40533     setFromData: function(v)
40534     {
40535         // this recieves an object, if setValues is called.
40536         this.reset();
40537         this.el.dom.value = v[this.displayField];
40538         this.hiddenEl.dom.value = v[this.valueField];
40539         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40540             return;
40541         }
40542         var kv = v[this.valueField];
40543         var dv = v[this.displayField];
40544         kv = typeof(kv) != 'string' ? '' : kv;
40545         dv = typeof(dv) != 'string' ? '' : dv;
40546         
40547         
40548         var keys = kv.split(',');
40549         var display = dv.split(',');
40550         for (var i = 0 ; i < keys.length; i++) {
40551             
40552             add = {};
40553             add[this.valueField] = keys[i];
40554             add[this.displayField] = display[i];
40555             this.addItem(add);
40556         }
40557       
40558         
40559     },
40560     
40561     /**
40562      * Validates the combox array value
40563      * @return {Boolean} True if the value is valid, else false
40564      */
40565     validate : function(){
40566         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40567             this.clearInvalid();
40568             return true;
40569         }
40570         return false;
40571     },
40572     
40573     validateValue : function(value){
40574         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40575         
40576     },
40577     
40578     /*@
40579      * overide
40580      * 
40581      */
40582     isDirty : function() {
40583         if(this.disabled) {
40584             return false;
40585         }
40586         
40587         try {
40588             var d = Roo.decode(String(this.originalValue));
40589         } catch (e) {
40590             return String(this.getValue()) !== String(this.originalValue);
40591         }
40592         
40593         var originalValue = [];
40594         
40595         for (var i = 0; i < d.length; i++){
40596             originalValue.push(d[i][this.valueField]);
40597         }
40598         
40599         return String(this.getValue()) !== String(originalValue.join(','));
40600         
40601     }
40602     
40603 });
40604
40605
40606
40607 /**
40608  * @class Roo.form.ComboBoxArray.Item
40609  * @extends Roo.BoxComponent
40610  * A selected item in the list
40611  *  Fred [x]  Brian [x]  [Pick another |v]
40612  * 
40613  * @constructor
40614  * Create a new item.
40615  * @param {Object} config Configuration options
40616  */
40617  
40618 Roo.form.ComboBoxArray.Item = function(config) {
40619     config.id = Roo.id();
40620     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40621 }
40622
40623 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40624     data : {},
40625     cb: false,
40626     displayField : false,
40627     tipField : false,
40628     
40629     
40630     defaultAutoCreate : {
40631         tag: 'div',
40632         cls: 'x-cbarray-item',
40633         cn : [ 
40634             { tag: 'div' },
40635             {
40636                 tag: 'img',
40637                 width:16,
40638                 height : 16,
40639                 src : Roo.BLANK_IMAGE_URL ,
40640                 align: 'center'
40641             }
40642         ]
40643         
40644     },
40645     
40646  
40647     onRender : function(ct, position)
40648     {
40649         Roo.form.Field.superclass.onRender.call(this, ct, position);
40650         
40651         if(!this.el){
40652             var cfg = this.getAutoCreate();
40653             this.el = ct.createChild(cfg, position);
40654         }
40655         
40656         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40657         
40658         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40659             this.cb.renderer(this.data) :
40660             String.format('{0}',this.data[this.displayField]);
40661         
40662             
40663         this.el.child('div').dom.setAttribute('qtip',
40664                         String.format('{0}',this.data[this.tipField])
40665         );
40666         
40667         this.el.child('img').on('click', this.remove, this);
40668         
40669     },
40670    
40671     remove : function()
40672     {
40673         this.cb.items.remove(this);
40674         this.el.child('img').un('click', this.remove, this);
40675         this.el.remove();
40676         this.cb.updateHiddenEl();
40677         
40678         this.cb.fireEvent('remove', this.cb, this);
40679     }
40680 });/*
40681  * Based on:
40682  * Ext JS Library 1.1.1
40683  * Copyright(c) 2006-2007, Ext JS, LLC.
40684  *
40685  * Originally Released Under LGPL - original licence link has changed is not relivant.
40686  *
40687  * Fork - LGPL
40688  * <script type="text/javascript">
40689  */
40690 /**
40691  * @class Roo.form.Checkbox
40692  * @extends Roo.form.Field
40693  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40694  * @constructor
40695  * Creates a new Checkbox
40696  * @param {Object} config Configuration options
40697  */
40698 Roo.form.Checkbox = function(config){
40699     Roo.form.Checkbox.superclass.constructor.call(this, config);
40700     this.addEvents({
40701         /**
40702          * @event check
40703          * Fires when the checkbox is checked or unchecked.
40704              * @param {Roo.form.Checkbox} this This checkbox
40705              * @param {Boolean} checked The new checked value
40706              */
40707         check : true
40708     });
40709 };
40710
40711 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40712     /**
40713      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40714      */
40715     focusClass : undefined,
40716     /**
40717      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40718      */
40719     fieldClass: "x-form-field",
40720     /**
40721      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40722      */
40723     checked: false,
40724     /**
40725      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40726      * {tag: "input", type: "checkbox", autocomplete: "off"})
40727      */
40728     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40729     /**
40730      * @cfg {String} boxLabel The text that appears beside the checkbox
40731      */
40732     boxLabel : "",
40733     /**
40734      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40735      */  
40736     inputValue : '1',
40737     /**
40738      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40739      */
40740      valueOff: '0', // value when not checked..
40741
40742     actionMode : 'viewEl', 
40743     //
40744     // private
40745     itemCls : 'x-menu-check-item x-form-item',
40746     groupClass : 'x-menu-group-item',
40747     inputType : 'hidden',
40748     
40749     
40750     inSetChecked: false, // check that we are not calling self...
40751     
40752     inputElement: false, // real input element?
40753     basedOn: false, // ????
40754     
40755     isFormField: true, // not sure where this is needed!!!!
40756
40757     onResize : function(){
40758         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40759         if(!this.boxLabel){
40760             this.el.alignTo(this.wrap, 'c-c');
40761         }
40762     },
40763
40764     initEvents : function(){
40765         Roo.form.Checkbox.superclass.initEvents.call(this);
40766         this.el.on("click", this.onClick,  this);
40767         this.el.on("change", this.onClick,  this);
40768     },
40769
40770
40771     getResizeEl : function(){
40772         return this.wrap;
40773     },
40774
40775     getPositionEl : function(){
40776         return this.wrap;
40777     },
40778
40779     // private
40780     onRender : function(ct, position){
40781         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40782         /*
40783         if(this.inputValue !== undefined){
40784             this.el.dom.value = this.inputValue;
40785         }
40786         */
40787         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40788         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40789         var viewEl = this.wrap.createChild({ 
40790             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40791         this.viewEl = viewEl;   
40792         this.wrap.on('click', this.onClick,  this); 
40793         
40794         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40795         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40796         
40797         
40798         
40799         if(this.boxLabel){
40800             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40801         //    viewEl.on('click', this.onClick,  this); 
40802         }
40803         //if(this.checked){
40804             this.setChecked(this.checked);
40805         //}else{
40806             //this.checked = this.el.dom;
40807         //}
40808
40809     },
40810
40811     // private
40812     initValue : Roo.emptyFn,
40813
40814     /**
40815      * Returns the checked state of the checkbox.
40816      * @return {Boolean} True if checked, else false
40817      */
40818     getValue : function(){
40819         if(this.el){
40820             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40821         }
40822         return this.valueOff;
40823         
40824     },
40825
40826         // private
40827     onClick : function(){ 
40828         this.setChecked(!this.checked);
40829
40830         //if(this.el.dom.checked != this.checked){
40831         //    this.setValue(this.el.dom.checked);
40832        // }
40833     },
40834
40835     /**
40836      * Sets the checked state of the checkbox.
40837      * On is always based on a string comparison between inputValue and the param.
40838      * @param {Boolean/String} value - the value to set 
40839      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40840      */
40841     setValue : function(v,suppressEvent){
40842         
40843         
40844         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40845         //if(this.el && this.el.dom){
40846         //    this.el.dom.checked = this.checked;
40847         //    this.el.dom.defaultChecked = this.checked;
40848         //}
40849         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40850         //this.fireEvent("check", this, this.checked);
40851     },
40852     // private..
40853     setChecked : function(state,suppressEvent)
40854     {
40855         if (this.inSetChecked) {
40856             this.checked = state;
40857             return;
40858         }
40859         
40860     
40861         if(this.wrap){
40862             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40863         }
40864         this.checked = state;
40865         if(suppressEvent !== true){
40866             this.fireEvent('check', this, state);
40867         }
40868         this.inSetChecked = true;
40869         this.el.dom.value = state ? this.inputValue : this.valueOff;
40870         this.inSetChecked = false;
40871         
40872     },
40873     // handle setting of hidden value by some other method!!?!?
40874     setFromHidden: function()
40875     {
40876         if(!this.el){
40877             return;
40878         }
40879         //console.log("SET FROM HIDDEN");
40880         //alert('setFrom hidden');
40881         this.setValue(this.el.dom.value);
40882     },
40883     
40884     onDestroy : function()
40885     {
40886         if(this.viewEl){
40887             Roo.get(this.viewEl).remove();
40888         }
40889          
40890         Roo.form.Checkbox.superclass.onDestroy.call(this);
40891     }
40892
40893 });/*
40894  * Based on:
40895  * Ext JS Library 1.1.1
40896  * Copyright(c) 2006-2007, Ext JS, LLC.
40897  *
40898  * Originally Released Under LGPL - original licence link has changed is not relivant.
40899  *
40900  * Fork - LGPL
40901  * <script type="text/javascript">
40902  */
40903  
40904 /**
40905  * @class Roo.form.Radio
40906  * @extends Roo.form.Checkbox
40907  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40908  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40909  * @constructor
40910  * Creates a new Radio
40911  * @param {Object} config Configuration options
40912  */
40913 Roo.form.Radio = function(){
40914     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40915 };
40916 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40917     inputType: 'radio',
40918
40919     /**
40920      * If this radio is part of a group, it will return the selected value
40921      * @return {String}
40922      */
40923     getGroupValue : function(){
40924         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40925     },
40926     
40927     
40928     onRender : function(ct, position){
40929         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40930         
40931         if(this.inputValue !== undefined){
40932             this.el.dom.value = this.inputValue;
40933         }
40934          
40935         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40936         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40937         //var viewEl = this.wrap.createChild({ 
40938         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40939         //this.viewEl = viewEl;   
40940         //this.wrap.on('click', this.onClick,  this); 
40941         
40942         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40943         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40944         
40945         
40946         
40947         if(this.boxLabel){
40948             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40949         //    viewEl.on('click', this.onClick,  this); 
40950         }
40951          if(this.checked){
40952             this.el.dom.checked =   'checked' ;
40953         }
40954          
40955     } 
40956     
40957     
40958 });//<script type="text/javascript">
40959
40960 /*
40961  * Based  Ext JS Library 1.1.1
40962  * Copyright(c) 2006-2007, Ext JS, LLC.
40963  * LGPL
40964  *
40965  */
40966  
40967 /**
40968  * @class Roo.HtmlEditorCore
40969  * @extends Roo.Component
40970  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
40971  *
40972  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40973  */
40974
40975 Roo.HtmlEditorCore = function(config){
40976     
40977     
40978     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
40979     this.addEvents({
40980         /**
40981          * @event initialize
40982          * Fires when the editor is fully initialized (including the iframe)
40983          * @param {Roo.HtmlEditorCore} this
40984          */
40985         initialize: true,
40986         /**
40987          * @event activate
40988          * Fires when the editor is first receives the focus. Any insertion must wait
40989          * until after this event.
40990          * @param {Roo.HtmlEditorCore} this
40991          */
40992         activate: true,
40993          /**
40994          * @event beforesync
40995          * Fires before the textarea is updated with content from the editor iframe. Return false
40996          * to cancel the sync.
40997          * @param {Roo.HtmlEditorCore} this
40998          * @param {String} html
40999          */
41000         beforesync: true,
41001          /**
41002          * @event beforepush
41003          * Fires before the iframe editor is updated with content from the textarea. Return false
41004          * to cancel the push.
41005          * @param {Roo.HtmlEditorCore} this
41006          * @param {String} html
41007          */
41008         beforepush: true,
41009          /**
41010          * @event sync
41011          * Fires when the textarea is updated with content from the editor iframe.
41012          * @param {Roo.HtmlEditorCore} this
41013          * @param {String} html
41014          */
41015         sync: true,
41016          /**
41017          * @event push
41018          * Fires when the iframe editor is updated with content from the textarea.
41019          * @param {Roo.HtmlEditorCore} this
41020          * @param {String} html
41021          */
41022         push: true,
41023         
41024         /**
41025          * @event editorevent
41026          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41027          * @param {Roo.HtmlEditorCore} this
41028          */
41029         editorevent: true
41030     });
41031      
41032 };
41033
41034
41035 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41036
41037
41038      /**
41039      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41040      */
41041     
41042     owner : false,
41043     
41044      /**
41045      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41046      *                        Roo.resizable.
41047      */
41048     resizable : false,
41049      /**
41050      * @cfg {Number} height (in pixels)
41051      */   
41052     height: 300,
41053    /**
41054      * @cfg {Number} width (in pixels)
41055      */   
41056     width: 500,
41057     
41058     /**
41059      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41060      * 
41061      */
41062     stylesheets: false,
41063     
41064     // id of frame..
41065     frameId: false,
41066     
41067     // private properties
41068     validationEvent : false,
41069     deferHeight: true,
41070     initialized : false,
41071     activated : false,
41072     sourceEditMode : false,
41073     onFocus : Roo.emptyFn,
41074     iframePad:3,
41075     hideMode:'offsets',
41076     
41077     clearUp: true,
41078     
41079      
41080     
41081
41082     /**
41083      * Protected method that will not generally be called directly. It
41084      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41085      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41086      */
41087     getDocMarkup : function(){
41088         // body styles..
41089         var st = '';
41090         Roo.log(this.stylesheets);
41091         
41092         // inherit styels from page...?? 
41093         if (this.stylesheets === false) {
41094             
41095             Roo.get(document.head).select('style').each(function(node) {
41096                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41097             });
41098             
41099             Roo.get(document.head).select('link').each(function(node) { 
41100                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41101             });
41102             
41103         } else if (!this.stylesheets.length) {
41104                 // simple..
41105                 st = '<style type="text/css">' +
41106                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41107                    '</style>';
41108         } else {
41109             Roo.each(this.stylesheets, function(s) {
41110                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41111             });
41112             
41113         }
41114         
41115         st +=  '<style type="text/css">' +
41116             'IMG { cursor: pointer } ' +
41117         '</style>';
41118
41119         
41120         return '<html><head>' + st  +
41121             //<style type="text/css">' +
41122             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41123             //'</style>' +
41124             ' </head><body class="roo-htmleditor-body"></body></html>';
41125     },
41126
41127     // private
41128     onRender : function(ct, position)
41129     {
41130         var _t = this;
41131         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41132         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41133         
41134         
41135         this.el.dom.style.border = '0 none';
41136         this.el.dom.setAttribute('tabIndex', -1);
41137         this.el.addClass('x-hidden hide');
41138         
41139         
41140         
41141         if(Roo.isIE){ // fix IE 1px bogus margin
41142             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41143         }
41144        
41145         
41146         this.frameId = Roo.id();
41147         
41148          
41149         
41150         var iframe = this.owner.wrap.createChild({
41151             tag: 'iframe',
41152             cls: 'form-control', // bootstrap..
41153             id: this.frameId,
41154             name: this.frameId,
41155             frameBorder : 'no',
41156             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41157         }, this.el
41158         );
41159         
41160         
41161         this.iframe = iframe.dom;
41162
41163          this.assignDocWin();
41164         
41165         this.doc.designMode = 'on';
41166        
41167         this.doc.open();
41168         this.doc.write(this.getDocMarkup());
41169         this.doc.close();
41170
41171         
41172         var task = { // must defer to wait for browser to be ready
41173             run : function(){
41174                 //console.log("run task?" + this.doc.readyState);
41175                 this.assignDocWin();
41176                 if(this.doc.body || this.doc.readyState == 'complete'){
41177                     try {
41178                         this.doc.designMode="on";
41179                     } catch (e) {
41180                         return;
41181                     }
41182                     Roo.TaskMgr.stop(task);
41183                     this.initEditor.defer(10, this);
41184                 }
41185             },
41186             interval : 10,
41187             duration: 10000,
41188             scope: this
41189         };
41190         Roo.TaskMgr.start(task);
41191
41192         
41193          
41194     },
41195
41196     // private
41197     onResize : function(w, h)
41198     {
41199          Roo.log('resize: ' +w + ',' + h );
41200         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41201         if(!this.iframe){
41202             return;
41203         }
41204         if(typeof w == 'number'){
41205             
41206             this.iframe.style.width = w + 'px';
41207         }
41208         if(typeof h == 'number'){
41209             
41210             this.iframe.style.height = h + 'px';
41211             if(this.doc){
41212                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41213             }
41214         }
41215         
41216     },
41217
41218     /**
41219      * Toggles the editor between standard and source edit mode.
41220      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41221      */
41222     toggleSourceEdit : function(sourceEditMode){
41223         
41224         this.sourceEditMode = sourceEditMode === true;
41225         
41226         if(this.sourceEditMode){
41227  
41228             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41229             
41230         }else{
41231             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41232             //this.iframe.className = '';
41233             this.deferFocus();
41234         }
41235         //this.setSize(this.owner.wrap.getSize());
41236         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41237     },
41238
41239     
41240   
41241
41242     /**
41243      * Protected method that will not generally be called directly. If you need/want
41244      * custom HTML cleanup, this is the method you should override.
41245      * @param {String} html The HTML to be cleaned
41246      * return {String} The cleaned HTML
41247      */
41248     cleanHtml : function(html){
41249         html = String(html);
41250         if(html.length > 5){
41251             if(Roo.isSafari){ // strip safari nonsense
41252                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41253             }
41254         }
41255         if(html == '&nbsp;'){
41256             html = '';
41257         }
41258         return html;
41259     },
41260
41261     /**
41262      * HTML Editor -> Textarea
41263      * Protected method that will not generally be called directly. Syncs the contents
41264      * of the editor iframe with the textarea.
41265      */
41266     syncValue : function(){
41267         if(this.initialized){
41268             var bd = (this.doc.body || this.doc.documentElement);
41269             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41270             var html = bd.innerHTML;
41271             if(Roo.isSafari){
41272                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41273                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41274                 if(m && m[1]){
41275                     html = '<div style="'+m[0]+'">' + html + '</div>';
41276                 }
41277             }
41278             html = this.cleanHtml(html);
41279             // fix up the special chars.. normaly like back quotes in word...
41280             // however we do not want to do this with chinese..
41281             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41282                 var cc = b.charCodeAt();
41283                 if (
41284                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41285                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41286                     (cc >= 0xf900 && cc < 0xfb00 )
41287                 ) {
41288                         return b;
41289                 }
41290                 return "&#"+cc+";" 
41291             });
41292             if(this.owner.fireEvent('beforesync', this, html) !== false){
41293                 this.el.dom.value = html;
41294                 this.owner.fireEvent('sync', this, html);
41295             }
41296         }
41297     },
41298
41299     /**
41300      * Protected method that will not generally be called directly. Pushes the value of the textarea
41301      * into the iframe editor.
41302      */
41303     pushValue : function(){
41304         if(this.initialized){
41305             var v = this.el.dom.value.trim();
41306             
41307 //            if(v.length < 1){
41308 //                v = '&#160;';
41309 //            }
41310             
41311             if(this.owner.fireEvent('beforepush', this, v) !== false){
41312                 var d = (this.doc.body || this.doc.documentElement);
41313                 d.innerHTML = v;
41314                 this.cleanUpPaste();
41315                 this.el.dom.value = d.innerHTML;
41316                 this.owner.fireEvent('push', this, v);
41317             }
41318         }
41319     },
41320
41321     // private
41322     deferFocus : function(){
41323         this.focus.defer(10, this);
41324     },
41325
41326     // doc'ed in Field
41327     focus : function(){
41328         if(this.win && !this.sourceEditMode){
41329             this.win.focus();
41330         }else{
41331             this.el.focus();
41332         }
41333     },
41334     
41335     assignDocWin: function()
41336     {
41337         var iframe = this.iframe;
41338         
41339          if(Roo.isIE){
41340             this.doc = iframe.contentWindow.document;
41341             this.win = iframe.contentWindow;
41342         } else {
41343             if (!Roo.get(this.frameId)) {
41344                 return;
41345             }
41346             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41347             this.win = Roo.get(this.frameId).dom.contentWindow;
41348         }
41349     },
41350     
41351     // private
41352     initEditor : function(){
41353         //console.log("INIT EDITOR");
41354         this.assignDocWin();
41355         
41356         
41357         
41358         this.doc.designMode="on";
41359         this.doc.open();
41360         this.doc.write(this.getDocMarkup());
41361         this.doc.close();
41362         
41363         var dbody = (this.doc.body || this.doc.documentElement);
41364         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41365         // this copies styles from the containing element into thsi one..
41366         // not sure why we need all of this..
41367         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41368         
41369         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41370         //ss['background-attachment'] = 'fixed'; // w3c
41371         dbody.bgProperties = 'fixed'; // ie
41372         //Roo.DomHelper.applyStyles(dbody, ss);
41373         Roo.EventManager.on(this.doc, {
41374             //'mousedown': this.onEditorEvent,
41375             'mouseup': this.onEditorEvent,
41376             'dblclick': this.onEditorEvent,
41377             'click': this.onEditorEvent,
41378             'keyup': this.onEditorEvent,
41379             buffer:100,
41380             scope: this
41381         });
41382         if(Roo.isGecko){
41383             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41384         }
41385         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41386             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41387         }
41388         this.initialized = true;
41389
41390         this.owner.fireEvent('initialize', this);
41391         this.pushValue();
41392     },
41393
41394     // private
41395     onDestroy : function(){
41396         
41397         
41398         
41399         if(this.rendered){
41400             
41401             //for (var i =0; i < this.toolbars.length;i++) {
41402             //    // fixme - ask toolbars for heights?
41403             //    this.toolbars[i].onDestroy();
41404            // }
41405             
41406             //this.wrap.dom.innerHTML = '';
41407             //this.wrap.remove();
41408         }
41409     },
41410
41411     // private
41412     onFirstFocus : function(){
41413         
41414         this.assignDocWin();
41415         
41416         
41417         this.activated = true;
41418          
41419     
41420         if(Roo.isGecko){ // prevent silly gecko errors
41421             this.win.focus();
41422             var s = this.win.getSelection();
41423             if(!s.focusNode || s.focusNode.nodeType != 3){
41424                 var r = s.getRangeAt(0);
41425                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41426                 r.collapse(true);
41427                 this.deferFocus();
41428             }
41429             try{
41430                 this.execCmd('useCSS', true);
41431                 this.execCmd('styleWithCSS', false);
41432             }catch(e){}
41433         }
41434         this.owner.fireEvent('activate', this);
41435     },
41436
41437     // private
41438     adjustFont: function(btn){
41439         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41440         //if(Roo.isSafari){ // safari
41441         //    adjust *= 2;
41442        // }
41443         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41444         if(Roo.isSafari){ // safari
41445             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41446             v =  (v < 10) ? 10 : v;
41447             v =  (v > 48) ? 48 : v;
41448             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41449             
41450         }
41451         
41452         
41453         v = Math.max(1, v+adjust);
41454         
41455         this.execCmd('FontSize', v  );
41456     },
41457
41458     onEditorEvent : function(e){
41459         this.owner.fireEvent('editorevent', this, e);
41460       //  this.updateToolbar();
41461         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41462     },
41463
41464     insertTag : function(tg)
41465     {
41466         // could be a bit smarter... -> wrap the current selected tRoo..
41467         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41468             
41469             range = this.createRange(this.getSelection());
41470             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41471             wrappingNode.appendChild(range.extractContents());
41472             range.insertNode(wrappingNode);
41473
41474             return;
41475             
41476             
41477             
41478         }
41479         this.execCmd("formatblock",   tg);
41480         
41481     },
41482     
41483     insertText : function(txt)
41484     {
41485         
41486         
41487         var range = this.createRange();
41488         range.deleteContents();
41489                //alert(Sender.getAttribute('label'));
41490                
41491         range.insertNode(this.doc.createTextNode(txt));
41492     } ,
41493     
41494      
41495
41496     /**
41497      * Executes a Midas editor command on the editor document and performs necessary focus and
41498      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41499      * @param {String} cmd The Midas command
41500      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41501      */
41502     relayCmd : function(cmd, value){
41503         this.win.focus();
41504         this.execCmd(cmd, value);
41505         this.owner.fireEvent('editorevent', this);
41506         //this.updateToolbar();
41507         this.owner.deferFocus();
41508     },
41509
41510     /**
41511      * Executes a Midas editor command directly on the editor document.
41512      * For visual commands, you should use {@link #relayCmd} instead.
41513      * <b>This should only be called after the editor is initialized.</b>
41514      * @param {String} cmd The Midas command
41515      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41516      */
41517     execCmd : function(cmd, value){
41518         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41519         this.syncValue();
41520     },
41521  
41522  
41523    
41524     /**
41525      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41526      * to insert tRoo.
41527      * @param {String} text | dom node.. 
41528      */
41529     insertAtCursor : function(text)
41530     {
41531         
41532         
41533         
41534         if(!this.activated){
41535             return;
41536         }
41537         /*
41538         if(Roo.isIE){
41539             this.win.focus();
41540             var r = this.doc.selection.createRange();
41541             if(r){
41542                 r.collapse(true);
41543                 r.pasteHTML(text);
41544                 this.syncValue();
41545                 this.deferFocus();
41546             
41547             }
41548             return;
41549         }
41550         */
41551         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41552             this.win.focus();
41553             
41554             
41555             // from jquery ui (MIT licenced)
41556             var range, node;
41557             var win = this.win;
41558             
41559             if (win.getSelection && win.getSelection().getRangeAt) {
41560                 range = win.getSelection().getRangeAt(0);
41561                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41562                 range.insertNode(node);
41563             } else if (win.document.selection && win.document.selection.createRange) {
41564                 // no firefox support
41565                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41566                 win.document.selection.createRange().pasteHTML(txt);
41567             } else {
41568                 // no firefox support
41569                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41570                 this.execCmd('InsertHTML', txt);
41571             } 
41572             
41573             this.syncValue();
41574             
41575             this.deferFocus();
41576         }
41577     },
41578  // private
41579     mozKeyPress : function(e){
41580         if(e.ctrlKey){
41581             var c = e.getCharCode(), cmd;
41582           
41583             if(c > 0){
41584                 c = String.fromCharCode(c).toLowerCase();
41585                 switch(c){
41586                     case 'b':
41587                         cmd = 'bold';
41588                         break;
41589                     case 'i':
41590                         cmd = 'italic';
41591                         break;
41592                     
41593                     case 'u':
41594                         cmd = 'underline';
41595                         break;
41596                     
41597                     case 'v':
41598                         this.cleanUpPaste.defer(100, this);
41599                         return;
41600                         
41601                 }
41602                 if(cmd){
41603                     this.win.focus();
41604                     this.execCmd(cmd);
41605                     this.deferFocus();
41606                     e.preventDefault();
41607                 }
41608                 
41609             }
41610         }
41611     },
41612
41613     // private
41614     fixKeys : function(){ // load time branching for fastest keydown performance
41615         if(Roo.isIE){
41616             return function(e){
41617                 var k = e.getKey(), r;
41618                 if(k == e.TAB){
41619                     e.stopEvent();
41620                     r = this.doc.selection.createRange();
41621                     if(r){
41622                         r.collapse(true);
41623                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41624                         this.deferFocus();
41625                     }
41626                     return;
41627                 }
41628                 
41629                 if(k == e.ENTER){
41630                     r = this.doc.selection.createRange();
41631                     if(r){
41632                         var target = r.parentElement();
41633                         if(!target || target.tagName.toLowerCase() != 'li'){
41634                             e.stopEvent();
41635                             r.pasteHTML('<br />');
41636                             r.collapse(false);
41637                             r.select();
41638                         }
41639                     }
41640                 }
41641                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41642                     this.cleanUpPaste.defer(100, this);
41643                     return;
41644                 }
41645                 
41646                 
41647             };
41648         }else if(Roo.isOpera){
41649             return function(e){
41650                 var k = e.getKey();
41651                 if(k == e.TAB){
41652                     e.stopEvent();
41653                     this.win.focus();
41654                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41655                     this.deferFocus();
41656                 }
41657                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41658                     this.cleanUpPaste.defer(100, this);
41659                     return;
41660                 }
41661                 
41662             };
41663         }else if(Roo.isSafari){
41664             return function(e){
41665                 var k = e.getKey();
41666                 
41667                 if(k == e.TAB){
41668                     e.stopEvent();
41669                     this.execCmd('InsertText','\t');
41670                     this.deferFocus();
41671                     return;
41672                 }
41673                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41674                     this.cleanUpPaste.defer(100, this);
41675                     return;
41676                 }
41677                 
41678              };
41679         }
41680     }(),
41681     
41682     getAllAncestors: function()
41683     {
41684         var p = this.getSelectedNode();
41685         var a = [];
41686         if (!p) {
41687             a.push(p); // push blank onto stack..
41688             p = this.getParentElement();
41689         }
41690         
41691         
41692         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41693             a.push(p);
41694             p = p.parentNode;
41695         }
41696         a.push(this.doc.body);
41697         return a;
41698     },
41699     lastSel : false,
41700     lastSelNode : false,
41701     
41702     
41703     getSelection : function() 
41704     {
41705         this.assignDocWin();
41706         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41707     },
41708     
41709     getSelectedNode: function() 
41710     {
41711         // this may only work on Gecko!!!
41712         
41713         // should we cache this!!!!
41714         
41715         
41716         
41717          
41718         var range = this.createRange(this.getSelection()).cloneRange();
41719         
41720         if (Roo.isIE) {
41721             var parent = range.parentElement();
41722             while (true) {
41723                 var testRange = range.duplicate();
41724                 testRange.moveToElementText(parent);
41725                 if (testRange.inRange(range)) {
41726                     break;
41727                 }
41728                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41729                     break;
41730                 }
41731                 parent = parent.parentElement;
41732             }
41733             return parent;
41734         }
41735         
41736         // is ancestor a text element.
41737         var ac =  range.commonAncestorContainer;
41738         if (ac.nodeType == 3) {
41739             ac = ac.parentNode;
41740         }
41741         
41742         var ar = ac.childNodes;
41743          
41744         var nodes = [];
41745         var other_nodes = [];
41746         var has_other_nodes = false;
41747         for (var i=0;i<ar.length;i++) {
41748             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41749                 continue;
41750             }
41751             // fullly contained node.
41752             
41753             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41754                 nodes.push(ar[i]);
41755                 continue;
41756             }
41757             
41758             // probably selected..
41759             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41760                 other_nodes.push(ar[i]);
41761                 continue;
41762             }
41763             // outer..
41764             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41765                 continue;
41766             }
41767             
41768             
41769             has_other_nodes = true;
41770         }
41771         if (!nodes.length && other_nodes.length) {
41772             nodes= other_nodes;
41773         }
41774         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41775             return false;
41776         }
41777         
41778         return nodes[0];
41779     },
41780     createRange: function(sel)
41781     {
41782         // this has strange effects when using with 
41783         // top toolbar - not sure if it's a great idea.
41784         //this.editor.contentWindow.focus();
41785         if (typeof sel != "undefined") {
41786             try {
41787                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41788             } catch(e) {
41789                 return this.doc.createRange();
41790             }
41791         } else {
41792             return this.doc.createRange();
41793         }
41794     },
41795     getParentElement: function()
41796     {
41797         
41798         this.assignDocWin();
41799         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41800         
41801         var range = this.createRange(sel);
41802          
41803         try {
41804             var p = range.commonAncestorContainer;
41805             while (p.nodeType == 3) { // text node
41806                 p = p.parentNode;
41807             }
41808             return p;
41809         } catch (e) {
41810             return null;
41811         }
41812     
41813     },
41814     /***
41815      *
41816      * Range intersection.. the hard stuff...
41817      *  '-1' = before
41818      *  '0' = hits..
41819      *  '1' = after.
41820      *         [ -- selected range --- ]
41821      *   [fail]                        [fail]
41822      *
41823      *    basically..
41824      *      if end is before start or  hits it. fail.
41825      *      if start is after end or hits it fail.
41826      *
41827      *   if either hits (but other is outside. - then it's not 
41828      *   
41829      *    
41830      **/
41831     
41832     
41833     // @see http://www.thismuchiknow.co.uk/?p=64.
41834     rangeIntersectsNode : function(range, node)
41835     {
41836         var nodeRange = node.ownerDocument.createRange();
41837         try {
41838             nodeRange.selectNode(node);
41839         } catch (e) {
41840             nodeRange.selectNodeContents(node);
41841         }
41842     
41843         var rangeStartRange = range.cloneRange();
41844         rangeStartRange.collapse(true);
41845     
41846         var rangeEndRange = range.cloneRange();
41847         rangeEndRange.collapse(false);
41848     
41849         var nodeStartRange = nodeRange.cloneRange();
41850         nodeStartRange.collapse(true);
41851     
41852         var nodeEndRange = nodeRange.cloneRange();
41853         nodeEndRange.collapse(false);
41854     
41855         return rangeStartRange.compareBoundaryPoints(
41856                  Range.START_TO_START, nodeEndRange) == -1 &&
41857                rangeEndRange.compareBoundaryPoints(
41858                  Range.START_TO_START, nodeStartRange) == 1;
41859         
41860          
41861     },
41862     rangeCompareNode : function(range, node)
41863     {
41864         var nodeRange = node.ownerDocument.createRange();
41865         try {
41866             nodeRange.selectNode(node);
41867         } catch (e) {
41868             nodeRange.selectNodeContents(node);
41869         }
41870         
41871         
41872         range.collapse(true);
41873     
41874         nodeRange.collapse(true);
41875      
41876         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41877         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41878          
41879         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41880         
41881         var nodeIsBefore   =  ss == 1;
41882         var nodeIsAfter    = ee == -1;
41883         
41884         if (nodeIsBefore && nodeIsAfter)
41885             return 0; // outer
41886         if (!nodeIsBefore && nodeIsAfter)
41887             return 1; //right trailed.
41888         
41889         if (nodeIsBefore && !nodeIsAfter)
41890             return 2;  // left trailed.
41891         // fully contined.
41892         return 3;
41893     },
41894
41895     // private? - in a new class?
41896     cleanUpPaste :  function()
41897     {
41898         // cleans up the whole document..
41899         Roo.log('cleanuppaste');
41900         
41901         this.cleanUpChildren(this.doc.body);
41902         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41903         if (clean != this.doc.body.innerHTML) {
41904             this.doc.body.innerHTML = clean;
41905         }
41906         
41907     },
41908     
41909     cleanWordChars : function(input) {// change the chars to hex code
41910         var he = Roo.HtmlEditorCore;
41911         
41912         var output = input;
41913         Roo.each(he.swapCodes, function(sw) { 
41914             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41915             
41916             output = output.replace(swapper, sw[1]);
41917         });
41918         
41919         return output;
41920     },
41921     
41922     
41923     cleanUpChildren : function (n)
41924     {
41925         if (!n.childNodes.length) {
41926             return;
41927         }
41928         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41929            this.cleanUpChild(n.childNodes[i]);
41930         }
41931     },
41932     
41933     
41934         
41935     
41936     cleanUpChild : function (node)
41937     {
41938         var ed = this;
41939         //console.log(node);
41940         if (node.nodeName == "#text") {
41941             // clean up silly Windows -- stuff?
41942             return; 
41943         }
41944         if (node.nodeName == "#comment") {
41945             node.parentNode.removeChild(node);
41946             // clean up silly Windows -- stuff?
41947             return; 
41948         }
41949         
41950         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
41951             // remove node.
41952             node.parentNode.removeChild(node);
41953             return;
41954             
41955         }
41956         
41957         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41958         
41959         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41960         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41961         
41962         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41963         //    remove_keep_children = true;
41964         //}
41965         
41966         if (remove_keep_children) {
41967             this.cleanUpChildren(node);
41968             // inserts everything just before this node...
41969             while (node.childNodes.length) {
41970                 var cn = node.childNodes[0];
41971                 node.removeChild(cn);
41972                 node.parentNode.insertBefore(cn, node);
41973             }
41974             node.parentNode.removeChild(node);
41975             return;
41976         }
41977         
41978         if (!node.attributes || !node.attributes.length) {
41979             this.cleanUpChildren(node);
41980             return;
41981         }
41982         
41983         function cleanAttr(n,v)
41984         {
41985             
41986             if (v.match(/^\./) || v.match(/^\//)) {
41987                 return;
41988             }
41989             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41990                 return;
41991             }
41992             if (v.match(/^#/)) {
41993                 return;
41994             }
41995 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41996             node.removeAttribute(n);
41997             
41998         }
41999         
42000         function cleanStyle(n,v)
42001         {
42002             if (v.match(/expression/)) { //XSS?? should we even bother..
42003                 node.removeAttribute(n);
42004                 return;
42005             }
42006             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
42007             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
42008             
42009             
42010             var parts = v.split(/;/);
42011             var clean = [];
42012             
42013             Roo.each(parts, function(p) {
42014                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42015                 if (!p.length) {
42016                     return true;
42017                 }
42018                 var l = p.split(':').shift().replace(/\s+/g,'');
42019                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42020                 
42021                 if ( cblack.indexOf(l) > -1) {
42022 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42023                     //node.removeAttribute(n);
42024                     return true;
42025                 }
42026                 //Roo.log()
42027                 // only allow 'c whitelisted system attributes'
42028                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42029 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42030                     //node.removeAttribute(n);
42031                     return true;
42032                 }
42033                 
42034                 
42035                  
42036                 
42037                 clean.push(p);
42038                 return true;
42039             });
42040             if (clean.length) { 
42041                 node.setAttribute(n, clean.join(';'));
42042             } else {
42043                 node.removeAttribute(n);
42044             }
42045             
42046         }
42047         
42048         
42049         for (var i = node.attributes.length-1; i > -1 ; i--) {
42050             var a = node.attributes[i];
42051             //console.log(a);
42052             
42053             if (a.name.toLowerCase().substr(0,2)=='on')  {
42054                 node.removeAttribute(a.name);
42055                 continue;
42056             }
42057             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42058                 node.removeAttribute(a.name);
42059                 continue;
42060             }
42061             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42062                 cleanAttr(a.name,a.value); // fixme..
42063                 continue;
42064             }
42065             if (a.name == 'style') {
42066                 cleanStyle(a.name,a.value);
42067                 continue;
42068             }
42069             /// clean up MS crap..
42070             // tecnically this should be a list of valid class'es..
42071             
42072             
42073             if (a.name == 'class') {
42074                 if (a.value.match(/^Mso/)) {
42075                     node.className = '';
42076                 }
42077                 
42078                 if (a.value.match(/body/)) {
42079                     node.className = '';
42080                 }
42081                 continue;
42082             }
42083             
42084             // style cleanup!?
42085             // class cleanup?
42086             
42087         }
42088         
42089         
42090         this.cleanUpChildren(node);
42091         
42092         
42093     },
42094     /**
42095      * Clean up MS wordisms...
42096      */
42097     cleanWord : function(node)
42098     {
42099         var _t = this;
42100         var cleanWordChildren = function()
42101         {
42102             if (!node.childNodes.length) {
42103                 return;
42104             }
42105             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42106                _t.cleanWord(node.childNodes[i]);
42107             }
42108         }
42109         
42110         
42111         if (!node) {
42112             this.cleanWord(this.doc.body);
42113             return;
42114         }
42115         if (node.nodeName == "#text") {
42116             // clean up silly Windows -- stuff?
42117             return; 
42118         }
42119         if (node.nodeName == "#comment") {
42120             node.parentNode.removeChild(node);
42121             // clean up silly Windows -- stuff?
42122             return; 
42123         }
42124         
42125         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42126             node.parentNode.removeChild(node);
42127             return;
42128         }
42129         
42130         // remove - but keep children..
42131         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42132             while (node.childNodes.length) {
42133                 var cn = node.childNodes[0];
42134                 node.removeChild(cn);
42135                 node.parentNode.insertBefore(cn, node);
42136             }
42137             node.parentNode.removeChild(node);
42138             cleanWordChildren();
42139             return;
42140         }
42141         // clean styles
42142         if (node.className.length) {
42143             
42144             var cn = node.className.split(/\W+/);
42145             var cna = [];
42146             Roo.each(cn, function(cls) {
42147                 if (cls.match(/Mso[a-zA-Z]+/)) {
42148                     return;
42149                 }
42150                 cna.push(cls);
42151             });
42152             node.className = cna.length ? cna.join(' ') : '';
42153             if (!cna.length) {
42154                 node.removeAttribute("class");
42155             }
42156         }
42157         
42158         if (node.hasAttribute("lang")) {
42159             node.removeAttribute("lang");
42160         }
42161         
42162         if (node.hasAttribute("style")) {
42163             
42164             var styles = node.getAttribute("style").split(";");
42165             var nstyle = [];
42166             Roo.each(styles, function(s) {
42167                 if (!s.match(/:/)) {
42168                     return;
42169                 }
42170                 var kv = s.split(":");
42171                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42172                     return;
42173                 }
42174                 // what ever is left... we allow.
42175                 nstyle.push(s);
42176             });
42177             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42178             if (!nstyle.length) {
42179                 node.removeAttribute('style');
42180             }
42181         }
42182         
42183         cleanWordChildren();
42184         
42185         
42186     },
42187     domToHTML : function(currentElement, depth, nopadtext) {
42188         
42189             depth = depth || 0;
42190             nopadtext = nopadtext || false;
42191         
42192             if (!currentElement) {
42193                 return this.domToHTML(this.doc.body);
42194             }
42195             
42196             //Roo.log(currentElement);
42197             var j;
42198             var allText = false;
42199             var nodeName = currentElement.nodeName;
42200             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42201             
42202             if  (nodeName == '#text') {
42203                 return currentElement.nodeValue;
42204             }
42205             
42206             
42207             var ret = '';
42208             if (nodeName != 'BODY') {
42209                  
42210                 var i = 0;
42211                 // Prints the node tagName, such as <A>, <IMG>, etc
42212                 if (tagName) {
42213                     var attr = [];
42214                     for(i = 0; i < currentElement.attributes.length;i++) {
42215                         // quoting?
42216                         var aname = currentElement.attributes.item(i).name;
42217                         if (!currentElement.attributes.item(i).value.length) {
42218                             continue;
42219                         }
42220                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42221                     }
42222                     
42223                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42224                 } 
42225                 else {
42226                     
42227                     // eack
42228                 }
42229             } else {
42230                 tagName = false;
42231             }
42232             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42233                 return ret;
42234             }
42235             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42236                 nopadtext = true;
42237             }
42238             
42239             
42240             // Traverse the tree
42241             i = 0;
42242             var currentElementChild = currentElement.childNodes.item(i);
42243             var allText = true;
42244             var innerHTML  = '';
42245             lastnode = '';
42246             while (currentElementChild) {
42247                 // Formatting code (indent the tree so it looks nice on the screen)
42248                 var nopad = nopadtext;
42249                 if (lastnode == 'SPAN') {
42250                     nopad  = true;
42251                 }
42252                 // text
42253                 if  (currentElementChild.nodeName == '#text') {
42254                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42255                     if (!nopad && toadd.length > 80) {
42256                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42257                     }
42258                     innerHTML  += toadd;
42259                     
42260                     i++;
42261                     currentElementChild = currentElement.childNodes.item(i);
42262                     lastNode = '';
42263                     continue;
42264                 }
42265                 allText = false;
42266                 
42267                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42268                     
42269                 // Recursively traverse the tree structure of the child node
42270                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42271                 lastnode = currentElementChild.nodeName;
42272                 i++;
42273                 currentElementChild=currentElement.childNodes.item(i);
42274             }
42275             
42276             ret += innerHTML;
42277             
42278             if (!allText) {
42279                     // The remaining code is mostly for formatting the tree
42280                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42281             }
42282             
42283             
42284             if (tagName) {
42285                 ret+= "</"+tagName+">";
42286             }
42287             return ret;
42288             
42289         }
42290     
42291     // hide stuff that is not compatible
42292     /**
42293      * @event blur
42294      * @hide
42295      */
42296     /**
42297      * @event change
42298      * @hide
42299      */
42300     /**
42301      * @event focus
42302      * @hide
42303      */
42304     /**
42305      * @event specialkey
42306      * @hide
42307      */
42308     /**
42309      * @cfg {String} fieldClass @hide
42310      */
42311     /**
42312      * @cfg {String} focusClass @hide
42313      */
42314     /**
42315      * @cfg {String} autoCreate @hide
42316      */
42317     /**
42318      * @cfg {String} inputType @hide
42319      */
42320     /**
42321      * @cfg {String} invalidClass @hide
42322      */
42323     /**
42324      * @cfg {String} invalidText @hide
42325      */
42326     /**
42327      * @cfg {String} msgFx @hide
42328      */
42329     /**
42330      * @cfg {String} validateOnBlur @hide
42331      */
42332 });
42333
42334 Roo.HtmlEditorCore.white = [
42335         'area', 'br', 'img', 'input', 'hr', 'wbr',
42336         
42337        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42338        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42339        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42340        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42341        'table',   'ul',         'xmp', 
42342        
42343        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42344       'thead',   'tr', 
42345      
42346       'dir', 'menu', 'ol', 'ul', 'dl',
42347        
42348       'embed',  'object'
42349 ];
42350
42351
42352 Roo.HtmlEditorCore.black = [
42353     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42354         'applet', // 
42355         'base',   'basefont', 'bgsound', 'blink',  'body', 
42356         'frame',  'frameset', 'head',    'html',   'ilayer', 
42357         'iframe', 'layer',  'link',     'meta',    'object',   
42358         'script', 'style' ,'title',  'xml' // clean later..
42359 ];
42360 Roo.HtmlEditorCore.clean = [
42361     'script', 'style', 'title', 'xml'
42362 ];
42363 Roo.HtmlEditorCore.remove = [
42364     'font'
42365 ];
42366 // attributes..
42367
42368 Roo.HtmlEditorCore.ablack = [
42369     'on'
42370 ];
42371     
42372 Roo.HtmlEditorCore.aclean = [ 
42373     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42374 ];
42375
42376 // protocols..
42377 Roo.HtmlEditorCore.pwhite= [
42378         'http',  'https',  'mailto'
42379 ];
42380
42381 // white listed style attributes.
42382 Roo.HtmlEditorCore.cwhite= [
42383       //  'text-align', /// default is to allow most things..
42384       
42385          
42386 //        'font-size'//??
42387 ];
42388
42389 // black listed style attributes.
42390 Roo.HtmlEditorCore.cblack= [
42391       //  'font-size' -- this can be set by the project 
42392 ];
42393
42394
42395 Roo.HtmlEditorCore.swapCodes   =[ 
42396     [    8211, "--" ], 
42397     [    8212, "--" ], 
42398     [    8216,  "'" ],  
42399     [    8217, "'" ],  
42400     [    8220, '"' ],  
42401     [    8221, '"' ],  
42402     [    8226, "*" ],  
42403     [    8230, "..." ]
42404 ]; 
42405
42406     //<script type="text/javascript">
42407
42408 /*
42409  * Ext JS Library 1.1.1
42410  * Copyright(c) 2006-2007, Ext JS, LLC.
42411  * Licence LGPL
42412  * 
42413  */
42414  
42415  
42416 Roo.form.HtmlEditor = function(config){
42417     
42418     
42419     
42420     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42421     
42422     if (!this.toolbars) {
42423         this.toolbars = [];
42424     }
42425     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42426     
42427     
42428 };
42429
42430 /**
42431  * @class Roo.form.HtmlEditor
42432  * @extends Roo.form.Field
42433  * Provides a lightweight HTML Editor component.
42434  *
42435  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42436  * 
42437  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42438  * supported by this editor.</b><br/><br/>
42439  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42440  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42441  */
42442 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42443     /**
42444      * @cfg {Boolean} clearUp
42445      */
42446     clearUp : true,
42447       /**
42448      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42449      */
42450     toolbars : false,
42451    
42452      /**
42453      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42454      *                        Roo.resizable.
42455      */
42456     resizable : false,
42457      /**
42458      * @cfg {Number} height (in pixels)
42459      */   
42460     height: 300,
42461    /**
42462      * @cfg {Number} width (in pixels)
42463      */   
42464     width: 500,
42465     
42466     /**
42467      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42468      * 
42469      */
42470     stylesheets: false,
42471     
42472     // id of frame..
42473     frameId: false,
42474     
42475     // private properties
42476     validationEvent : false,
42477     deferHeight: true,
42478     initialized : false,
42479     activated : false,
42480     
42481     onFocus : Roo.emptyFn,
42482     iframePad:3,
42483     hideMode:'offsets',
42484     
42485     defaultAutoCreate : { // modified by initCompnoent..
42486         tag: "textarea",
42487         style:"width:500px;height:300px;",
42488         autocomplete: "off"
42489     },
42490
42491     // private
42492     initComponent : function(){
42493         this.addEvents({
42494             /**
42495              * @event initialize
42496              * Fires when the editor is fully initialized (including the iframe)
42497              * @param {HtmlEditor} this
42498              */
42499             initialize: true,
42500             /**
42501              * @event activate
42502              * Fires when the editor is first receives the focus. Any insertion must wait
42503              * until after this event.
42504              * @param {HtmlEditor} this
42505              */
42506             activate: true,
42507              /**
42508              * @event beforesync
42509              * Fires before the textarea is updated with content from the editor iframe. Return false
42510              * to cancel the sync.
42511              * @param {HtmlEditor} this
42512              * @param {String} html
42513              */
42514             beforesync: true,
42515              /**
42516              * @event beforepush
42517              * Fires before the iframe editor is updated with content from the textarea. Return false
42518              * to cancel the push.
42519              * @param {HtmlEditor} this
42520              * @param {String} html
42521              */
42522             beforepush: true,
42523              /**
42524              * @event sync
42525              * Fires when the textarea is updated with content from the editor iframe.
42526              * @param {HtmlEditor} this
42527              * @param {String} html
42528              */
42529             sync: true,
42530              /**
42531              * @event push
42532              * Fires when the iframe editor is updated with content from the textarea.
42533              * @param {HtmlEditor} this
42534              * @param {String} html
42535              */
42536             push: true,
42537              /**
42538              * @event editmodechange
42539              * Fires when the editor switches edit modes
42540              * @param {HtmlEditor} this
42541              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42542              */
42543             editmodechange: true,
42544             /**
42545              * @event editorevent
42546              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42547              * @param {HtmlEditor} this
42548              */
42549             editorevent: true,
42550             /**
42551              * @event firstfocus
42552              * Fires when on first focus - needed by toolbars..
42553              * @param {HtmlEditor} this
42554              */
42555             firstfocus: true,
42556             /**
42557              * @event autosave
42558              * Auto save the htmlEditor value as a file into Events
42559              * @param {HtmlEditor} this
42560              */
42561             autosave: true,
42562             /**
42563              * @event savedpreview
42564              * preview the saved version of htmlEditor
42565              * @param {HtmlEditor} this
42566              */
42567             savedpreview: true
42568         });
42569         this.defaultAutoCreate =  {
42570             tag: "textarea",
42571             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42572             autocomplete: "off"
42573         };
42574     },
42575
42576     /**
42577      * Protected method that will not generally be called directly. It
42578      * is called when the editor creates its toolbar. Override this method if you need to
42579      * add custom toolbar buttons.
42580      * @param {HtmlEditor} editor
42581      */
42582     createToolbar : function(editor){
42583         Roo.log("create toolbars");
42584         if (!editor.toolbars || !editor.toolbars.length) {
42585             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42586         }
42587         
42588         for (var i =0 ; i < editor.toolbars.length;i++) {
42589             editor.toolbars[i] = Roo.factory(
42590                     typeof(editor.toolbars[i]) == 'string' ?
42591                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42592                 Roo.form.HtmlEditor);
42593             editor.toolbars[i].init(editor);
42594         }
42595          
42596         
42597     },
42598
42599      
42600     // private
42601     onRender : function(ct, position)
42602     {
42603         var _t = this;
42604         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42605         
42606         this.wrap = this.el.wrap({
42607             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42608         });
42609         
42610         this.editorcore.onRender(ct, position);
42611          
42612         if (this.resizable) {
42613             this.resizeEl = new Roo.Resizable(this.wrap, {
42614                 pinned : true,
42615                 wrap: true,
42616                 dynamic : true,
42617                 minHeight : this.height,
42618                 height: this.height,
42619                 handles : this.resizable,
42620                 width: this.width,
42621                 listeners : {
42622                     resize : function(r, w, h) {
42623                         _t.onResize(w,h); // -something
42624                     }
42625                 }
42626             });
42627             
42628         }
42629         this.createToolbar(this);
42630        
42631         
42632         if(!this.width){
42633             this.setSize(this.wrap.getSize());
42634         }
42635         if (this.resizeEl) {
42636             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42637             // should trigger onReize..
42638         }
42639         
42640 //        if(this.autosave && this.w){
42641 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42642 //        }
42643     },
42644
42645     // private
42646     onResize : function(w, h)
42647     {
42648         //Roo.log('resize: ' +w + ',' + h );
42649         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42650         var ew = false;
42651         var eh = false;
42652         
42653         if(this.el ){
42654             if(typeof w == 'number'){
42655                 var aw = w - this.wrap.getFrameWidth('lr');
42656                 this.el.setWidth(this.adjustWidth('textarea', aw));
42657                 ew = aw;
42658             }
42659             if(typeof h == 'number'){
42660                 var tbh = 0;
42661                 for (var i =0; i < this.toolbars.length;i++) {
42662                     // fixme - ask toolbars for heights?
42663                     tbh += this.toolbars[i].tb.el.getHeight();
42664                     if (this.toolbars[i].footer) {
42665                         tbh += this.toolbars[i].footer.el.getHeight();
42666                     }
42667                 }
42668                 
42669                 
42670                 
42671                 
42672                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42673                 ah -= 5; // knock a few pixes off for look..
42674                 this.el.setHeight(this.adjustWidth('textarea', ah));
42675                 var eh = ah;
42676             }
42677         }
42678         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42679         this.editorcore.onResize(ew,eh);
42680         
42681     },
42682
42683     /**
42684      * Toggles the editor between standard and source edit mode.
42685      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42686      */
42687     toggleSourceEdit : function(sourceEditMode)
42688     {
42689         this.editorcore.toggleSourceEdit(sourceEditMode);
42690         
42691         if(this.editorcore.sourceEditMode){
42692             Roo.log('editor - showing textarea');
42693             
42694 //            Roo.log('in');
42695 //            Roo.log(this.syncValue());
42696             this.editorcore.syncValue();
42697             this.el.removeClass('x-hidden');
42698             this.el.dom.removeAttribute('tabIndex');
42699             this.el.focus();
42700         }else{
42701             Roo.log('editor - hiding textarea');
42702 //            Roo.log('out')
42703 //            Roo.log(this.pushValue()); 
42704             this.editorcore.pushValue();
42705             
42706             this.el.addClass('x-hidden');
42707             this.el.dom.setAttribute('tabIndex', -1);
42708             //this.deferFocus();
42709         }
42710          
42711         this.setSize(this.wrap.getSize());
42712         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42713     },
42714  
42715     // private (for BoxComponent)
42716     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42717
42718     // private (for BoxComponent)
42719     getResizeEl : function(){
42720         return this.wrap;
42721     },
42722
42723     // private (for BoxComponent)
42724     getPositionEl : function(){
42725         return this.wrap;
42726     },
42727
42728     // private
42729     initEvents : function(){
42730         this.originalValue = this.getValue();
42731     },
42732
42733     /**
42734      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42735      * @method
42736      */
42737     markInvalid : Roo.emptyFn,
42738     /**
42739      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42740      * @method
42741      */
42742     clearInvalid : Roo.emptyFn,
42743
42744     setValue : function(v){
42745         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42746         this.editorcore.pushValue();
42747     },
42748
42749      
42750     // private
42751     deferFocus : function(){
42752         this.focus.defer(10, this);
42753     },
42754
42755     // doc'ed in Field
42756     focus : function(){
42757         this.editorcore.focus();
42758         
42759     },
42760       
42761
42762     // private
42763     onDestroy : function(){
42764         
42765         
42766         
42767         if(this.rendered){
42768             
42769             for (var i =0; i < this.toolbars.length;i++) {
42770                 // fixme - ask toolbars for heights?
42771                 this.toolbars[i].onDestroy();
42772             }
42773             
42774             this.wrap.dom.innerHTML = '';
42775             this.wrap.remove();
42776         }
42777     },
42778
42779     // private
42780     onFirstFocus : function(){
42781         //Roo.log("onFirstFocus");
42782         this.editorcore.onFirstFocus();
42783          for (var i =0; i < this.toolbars.length;i++) {
42784             this.toolbars[i].onFirstFocus();
42785         }
42786         
42787     },
42788     
42789     // private
42790     syncValue : function()
42791     {
42792         this.editorcore.syncValue();
42793     },
42794     
42795     pushValue : function()
42796     {
42797         this.editorcore.pushValue();
42798     }
42799      
42800     
42801     // hide stuff that is not compatible
42802     /**
42803      * @event blur
42804      * @hide
42805      */
42806     /**
42807      * @event change
42808      * @hide
42809      */
42810     /**
42811      * @event focus
42812      * @hide
42813      */
42814     /**
42815      * @event specialkey
42816      * @hide
42817      */
42818     /**
42819      * @cfg {String} fieldClass @hide
42820      */
42821     /**
42822      * @cfg {String} focusClass @hide
42823      */
42824     /**
42825      * @cfg {String} autoCreate @hide
42826      */
42827     /**
42828      * @cfg {String} inputType @hide
42829      */
42830     /**
42831      * @cfg {String} invalidClass @hide
42832      */
42833     /**
42834      * @cfg {String} invalidText @hide
42835      */
42836     /**
42837      * @cfg {String} msgFx @hide
42838      */
42839     /**
42840      * @cfg {String} validateOnBlur @hide
42841      */
42842 });
42843  
42844     // <script type="text/javascript">
42845 /*
42846  * Based on
42847  * Ext JS Library 1.1.1
42848  * Copyright(c) 2006-2007, Ext JS, LLC.
42849  *  
42850  
42851  */
42852
42853 /**
42854  * @class Roo.form.HtmlEditorToolbar1
42855  * Basic Toolbar
42856  * 
42857  * Usage:
42858  *
42859  new Roo.form.HtmlEditor({
42860     ....
42861     toolbars : [
42862         new Roo.form.HtmlEditorToolbar1({
42863             disable : { fonts: 1 , format: 1, ..., ... , ...],
42864             btns : [ .... ]
42865         })
42866     }
42867      
42868  * 
42869  * @cfg {Object} disable List of elements to disable..
42870  * @cfg {Array} btns List of additional buttons.
42871  * 
42872  * 
42873  * NEEDS Extra CSS? 
42874  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42875  */
42876  
42877 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42878 {
42879     
42880     Roo.apply(this, config);
42881     
42882     // default disabled, based on 'good practice'..
42883     this.disable = this.disable || {};
42884     Roo.applyIf(this.disable, {
42885         fontSize : true,
42886         colors : true,
42887         specialElements : true
42888     });
42889     
42890     
42891     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42892     // dont call parent... till later.
42893 }
42894
42895 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42896     
42897     tb: false,
42898     
42899     rendered: false,
42900     
42901     editor : false,
42902     editorcore : false,
42903     /**
42904      * @cfg {Object} disable  List of toolbar elements to disable
42905          
42906      */
42907     disable : false,
42908     
42909     
42910      /**
42911      * @cfg {String} createLinkText The default text for the create link prompt
42912      */
42913     createLinkText : 'Please enter the URL for the link:',
42914     /**
42915      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42916      */
42917     defaultLinkValue : 'http:/'+'/',
42918    
42919     
42920       /**
42921      * @cfg {Array} fontFamilies An array of available font families
42922      */
42923     fontFamilies : [
42924         'Arial',
42925         'Courier New',
42926         'Tahoma',
42927         'Times New Roman',
42928         'Verdana'
42929     ],
42930     
42931     specialChars : [
42932            "&#169;",
42933           "&#174;",     
42934           "&#8482;",    
42935           "&#163;" ,    
42936          // "&#8212;",    
42937           "&#8230;",    
42938           "&#247;" ,    
42939         //  "&#225;" ,     ?? a acute?
42940            "&#8364;"    , //Euro
42941        //   "&#8220;"    ,
42942         //  "&#8221;"    ,
42943         //  "&#8226;"    ,
42944           "&#176;"  //   , // degrees
42945
42946          // "&#233;"     , // e ecute
42947          // "&#250;"     , // u ecute?
42948     ],
42949     
42950     specialElements : [
42951         {
42952             text: "Insert Table",
42953             xtype: 'MenuItem',
42954             xns : Roo.Menu,
42955             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42956                 
42957         },
42958         {    
42959             text: "Insert Image",
42960             xtype: 'MenuItem',
42961             xns : Roo.Menu,
42962             ihtml : '<img src="about:blank"/>'
42963             
42964         }
42965         
42966          
42967     ],
42968     
42969     
42970     inputElements : [ 
42971             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42972             "input:submit", "input:button", "select", "textarea", "label" ],
42973     formats : [
42974         ["p"] ,  
42975         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42976         ["pre"],[ "code"], 
42977         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42978         ['div'],['span']
42979     ],
42980     
42981     cleanStyles : [
42982         "font-size"
42983     ],
42984      /**
42985      * @cfg {String} defaultFont default font to use.
42986      */
42987     defaultFont: 'tahoma',
42988    
42989     fontSelect : false,
42990     
42991     
42992     formatCombo : false,
42993     
42994     init : function(editor)
42995     {
42996         this.editor = editor;
42997         this.editorcore = editor.editorcore ? editor.editorcore : editor;
42998         var editorcore = this.editorcore;
42999         
43000         var _t = this;
43001         
43002         var fid = editorcore.frameId;
43003         var etb = this;
43004         function btn(id, toggle, handler){
43005             var xid = fid + '-'+ id ;
43006             return {
43007                 id : xid,
43008                 cmd : id,
43009                 cls : 'x-btn-icon x-edit-'+id,
43010                 enableToggle:toggle !== false,
43011                 scope: _t, // was editor...
43012                 handler:handler||_t.relayBtnCmd,
43013                 clickEvent:'mousedown',
43014                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43015                 tabIndex:-1
43016             };
43017         }
43018         
43019         
43020         
43021         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43022         this.tb = tb;
43023          // stop form submits
43024         tb.el.on('click', function(e){
43025             e.preventDefault(); // what does this do?
43026         });
43027
43028         if(!this.disable.font) { // && !Roo.isSafari){
43029             /* why no safari for fonts 
43030             editor.fontSelect = tb.el.createChild({
43031                 tag:'select',
43032                 tabIndex: -1,
43033                 cls:'x-font-select',
43034                 html: this.createFontOptions()
43035             });
43036             
43037             editor.fontSelect.on('change', function(){
43038                 var font = editor.fontSelect.dom.value;
43039                 editor.relayCmd('fontname', font);
43040                 editor.deferFocus();
43041             }, editor);
43042             
43043             tb.add(
43044                 editor.fontSelect.dom,
43045                 '-'
43046             );
43047             */
43048             
43049         };
43050         if(!this.disable.formats){
43051             this.formatCombo = new Roo.form.ComboBox({
43052                 store: new Roo.data.SimpleStore({
43053                     id : 'tag',
43054                     fields: ['tag'],
43055                     data : this.formats // from states.js
43056                 }),
43057                 blockFocus : true,
43058                 name : '',
43059                 //autoCreate : {tag: "div",  size: "20"},
43060                 displayField:'tag',
43061                 typeAhead: false,
43062                 mode: 'local',
43063                 editable : false,
43064                 triggerAction: 'all',
43065                 emptyText:'Add tag',
43066                 selectOnFocus:true,
43067                 width:135,
43068                 listeners : {
43069                     'select': function(c, r, i) {
43070                         editorcore.insertTag(r.get('tag'));
43071                         editor.focus();
43072                     }
43073                 }
43074
43075             });
43076             tb.addField(this.formatCombo);
43077             
43078         }
43079         
43080         if(!this.disable.format){
43081             tb.add(
43082                 btn('bold'),
43083                 btn('italic'),
43084                 btn('underline')
43085             );
43086         };
43087         if(!this.disable.fontSize){
43088             tb.add(
43089                 '-',
43090                 
43091                 
43092                 btn('increasefontsize', false, editorcore.adjustFont),
43093                 btn('decreasefontsize', false, editorcore.adjustFont)
43094             );
43095         };
43096         
43097         
43098         if(!this.disable.colors){
43099             tb.add(
43100                 '-', {
43101                     id:editorcore.frameId +'-forecolor',
43102                     cls:'x-btn-icon x-edit-forecolor',
43103                     clickEvent:'mousedown',
43104                     tooltip: this.buttonTips['forecolor'] || undefined,
43105                     tabIndex:-1,
43106                     menu : new Roo.menu.ColorMenu({
43107                         allowReselect: true,
43108                         focus: Roo.emptyFn,
43109                         value:'000000',
43110                         plain:true,
43111                         selectHandler: function(cp, color){
43112                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43113                             editor.deferFocus();
43114                         },
43115                         scope: editorcore,
43116                         clickEvent:'mousedown'
43117                     })
43118                 }, {
43119                     id:editorcore.frameId +'backcolor',
43120                     cls:'x-btn-icon x-edit-backcolor',
43121                     clickEvent:'mousedown',
43122                     tooltip: this.buttonTips['backcolor'] || undefined,
43123                     tabIndex:-1,
43124                     menu : new Roo.menu.ColorMenu({
43125                         focus: Roo.emptyFn,
43126                         value:'FFFFFF',
43127                         plain:true,
43128                         allowReselect: true,
43129                         selectHandler: function(cp, color){
43130                             if(Roo.isGecko){
43131                                 editorcore.execCmd('useCSS', false);
43132                                 editorcore.execCmd('hilitecolor', color);
43133                                 editorcore.execCmd('useCSS', true);
43134                                 editor.deferFocus();
43135                             }else{
43136                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43137                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43138                                 editor.deferFocus();
43139                             }
43140                         },
43141                         scope:editorcore,
43142                         clickEvent:'mousedown'
43143                     })
43144                 }
43145             );
43146         };
43147         // now add all the items...
43148         
43149
43150         if(!this.disable.alignments){
43151             tb.add(
43152                 '-',
43153                 btn('justifyleft'),
43154                 btn('justifycenter'),
43155                 btn('justifyright')
43156             );
43157         };
43158
43159         //if(!Roo.isSafari){
43160             if(!this.disable.links){
43161                 tb.add(
43162                     '-',
43163                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43164                 );
43165             };
43166
43167             if(!this.disable.lists){
43168                 tb.add(
43169                     '-',
43170                     btn('insertorderedlist'),
43171                     btn('insertunorderedlist')
43172                 );
43173             }
43174             if(!this.disable.sourceEdit){
43175                 tb.add(
43176                     '-',
43177                     btn('sourceedit', true, function(btn){
43178                         Roo.log(this);
43179                         this.toggleSourceEdit(btn.pressed);
43180                     })
43181                 );
43182             }
43183         //}
43184         
43185         var smenu = { };
43186         // special menu.. - needs to be tidied up..
43187         if (!this.disable.special) {
43188             smenu = {
43189                 text: "&#169;",
43190                 cls: 'x-edit-none',
43191                 
43192                 menu : {
43193                     items : []
43194                 }
43195             };
43196             for (var i =0; i < this.specialChars.length; i++) {
43197                 smenu.menu.items.push({
43198                     
43199                     html: this.specialChars[i],
43200                     handler: function(a,b) {
43201                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43202                         //editor.insertAtCursor(a.html);
43203                         
43204                     },
43205                     tabIndex:-1
43206                 });
43207             }
43208             
43209             
43210             tb.add(smenu);
43211             
43212             
43213         }
43214         
43215         var cmenu = { };
43216         if (!this.disable.cleanStyles) {
43217             cmenu = {
43218                 cls: 'x-btn-icon x-btn-clear',
43219                 
43220                 menu : {
43221                     items : []
43222                 }
43223             };
43224             for (var i =0; i < this.cleanStyles.length; i++) {
43225                 cmenu.menu.items.push({
43226                     actiontype : this.cleanStyles[i],
43227                     html: 'Remove ' + this.cleanStyles[i],
43228                     handler: function(a,b) {
43229                         Roo.log(a);
43230                         Roo.log(b);
43231                         var c = Roo.get(editorcore.doc.body);
43232                         c.select('[style]').each(function(s) {
43233                             s.dom.style.removeProperty(a.actiontype);
43234                         });
43235                         editorcore.syncValue();
43236                     },
43237                     tabIndex:-1
43238                 });
43239             }
43240             cmenu.menu.items.push({
43241                 actiontype : 'word',
43242                 html: 'Remove MS Word Formating',
43243                 handler: function(a,b) {
43244                     editorcore.cleanWord();
43245                     editorcore.syncValue();
43246                 },
43247                 tabIndex:-1
43248             });
43249             
43250             cmenu.menu.items.push({
43251                 actiontype : 'all',
43252                 html: 'Remove All Styles',
43253                 handler: function(a,b) {
43254                     
43255                     var c = Roo.get(editorcore.doc.body);
43256                     c.select('[style]').each(function(s) {
43257                         s.dom.removeAttribute('style');
43258                     });
43259                     editorcore.syncValue();
43260                 },
43261                 tabIndex:-1
43262             });
43263              cmenu.menu.items.push({
43264                 actiontype : 'word',
43265                 html: 'Tidy HTML Source',
43266                 handler: function(a,b) {
43267                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43268                     editorcore.syncValue();
43269                 },
43270                 tabIndex:-1
43271             });
43272             
43273             
43274             tb.add(cmenu);
43275         }
43276          
43277         if (!this.disable.specialElements) {
43278             var semenu = {
43279                 text: "Other;",
43280                 cls: 'x-edit-none',
43281                 menu : {
43282                     items : []
43283                 }
43284             };
43285             for (var i =0; i < this.specialElements.length; i++) {
43286                 semenu.menu.items.push(
43287                     Roo.apply({ 
43288                         handler: function(a,b) {
43289                             editor.insertAtCursor(this.ihtml);
43290                         }
43291                     }, this.specialElements[i])
43292                 );
43293                     
43294             }
43295             
43296             tb.add(semenu);
43297             
43298             
43299         }
43300          
43301         
43302         if (this.btns) {
43303             for(var i =0; i< this.btns.length;i++) {
43304                 var b = Roo.factory(this.btns[i],Roo.form);
43305                 b.cls =  'x-edit-none';
43306                 b.scope = editorcore;
43307                 tb.add(b);
43308             }
43309         
43310         }
43311         
43312         
43313         
43314         // disable everything...
43315         
43316         this.tb.items.each(function(item){
43317            if(item.id != editorcore.frameId+ '-sourceedit'){
43318                 item.disable();
43319             }
43320         });
43321         this.rendered = true;
43322         
43323         // the all the btns;
43324         editor.on('editorevent', this.updateToolbar, this);
43325         // other toolbars need to implement this..
43326         //editor.on('editmodechange', this.updateToolbar, this);
43327     },
43328     
43329     
43330     relayBtnCmd : function(btn) {
43331         this.editorcore.relayCmd(btn.cmd);
43332     },
43333     // private used internally
43334     createLink : function(){
43335         Roo.log("create link?");
43336         var url = prompt(this.createLinkText, this.defaultLinkValue);
43337         if(url && url != 'http:/'+'/'){
43338             this.editorcore.relayCmd('createlink', url);
43339         }
43340     },
43341
43342     
43343     /**
43344      * Protected method that will not generally be called directly. It triggers
43345      * a toolbar update by reading the markup state of the current selection in the editor.
43346      */
43347     updateToolbar: function(){
43348
43349         if(!this.editorcore.activated){
43350             this.editor.onFirstFocus();
43351             return;
43352         }
43353
43354         var btns = this.tb.items.map, 
43355             doc = this.editorcore.doc,
43356             frameId = this.editorcore.frameId;
43357
43358         if(!this.disable.font && !Roo.isSafari){
43359             /*
43360             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43361             if(name != this.fontSelect.dom.value){
43362                 this.fontSelect.dom.value = name;
43363             }
43364             */
43365         }
43366         if(!this.disable.format){
43367             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43368             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43369             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43370         }
43371         if(!this.disable.alignments){
43372             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43373             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43374             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43375         }
43376         if(!Roo.isSafari && !this.disable.lists){
43377             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43378             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43379         }
43380         
43381         var ans = this.editorcore.getAllAncestors();
43382         if (this.formatCombo) {
43383             
43384             
43385             var store = this.formatCombo.store;
43386             this.formatCombo.setValue("");
43387             for (var i =0; i < ans.length;i++) {
43388                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43389                     // select it..
43390                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43391                     break;
43392                 }
43393             }
43394         }
43395         
43396         
43397         
43398         // hides menus... - so this cant be on a menu...
43399         Roo.menu.MenuMgr.hideAll();
43400
43401         //this.editorsyncValue();
43402     },
43403    
43404     
43405     createFontOptions : function(){
43406         var buf = [], fs = this.fontFamilies, ff, lc;
43407         
43408         
43409         
43410         for(var i = 0, len = fs.length; i< len; i++){
43411             ff = fs[i];
43412             lc = ff.toLowerCase();
43413             buf.push(
43414                 '<option value="',lc,'" style="font-family:',ff,';"',
43415                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43416                     ff,
43417                 '</option>'
43418             );
43419         }
43420         return buf.join('');
43421     },
43422     
43423     toggleSourceEdit : function(sourceEditMode){
43424         
43425         Roo.log("toolbar toogle");
43426         if(sourceEditMode === undefined){
43427             sourceEditMode = !this.sourceEditMode;
43428         }
43429         this.sourceEditMode = sourceEditMode === true;
43430         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43431         // just toggle the button?
43432         if(btn.pressed !== this.sourceEditMode){
43433             btn.toggle(this.sourceEditMode);
43434             return;
43435         }
43436         
43437         if(sourceEditMode){
43438             Roo.log("disabling buttons");
43439             this.tb.items.each(function(item){
43440                 if(item.cmd != 'sourceedit'){
43441                     item.disable();
43442                 }
43443             });
43444           
43445         }else{
43446             Roo.log("enabling buttons");
43447             if(this.editorcore.initialized){
43448                 this.tb.items.each(function(item){
43449                     item.enable();
43450                 });
43451             }
43452             
43453         }
43454         Roo.log("calling toggole on editor");
43455         // tell the editor that it's been pressed..
43456         this.editor.toggleSourceEdit(sourceEditMode);
43457        
43458     },
43459      /**
43460      * Object collection of toolbar tooltips for the buttons in the editor. The key
43461      * is the command id associated with that button and the value is a valid QuickTips object.
43462      * For example:
43463 <pre><code>
43464 {
43465     bold : {
43466         title: 'Bold (Ctrl+B)',
43467         text: 'Make the selected text bold.',
43468         cls: 'x-html-editor-tip'
43469     },
43470     italic : {
43471         title: 'Italic (Ctrl+I)',
43472         text: 'Make the selected text italic.',
43473         cls: 'x-html-editor-tip'
43474     },
43475     ...
43476 </code></pre>
43477     * @type Object
43478      */
43479     buttonTips : {
43480         bold : {
43481             title: 'Bold (Ctrl+B)',
43482             text: 'Make the selected text bold.',
43483             cls: 'x-html-editor-tip'
43484         },
43485         italic : {
43486             title: 'Italic (Ctrl+I)',
43487             text: 'Make the selected text italic.',
43488             cls: 'x-html-editor-tip'
43489         },
43490         underline : {
43491             title: 'Underline (Ctrl+U)',
43492             text: 'Underline the selected text.',
43493             cls: 'x-html-editor-tip'
43494         },
43495         increasefontsize : {
43496             title: 'Grow Text',
43497             text: 'Increase the font size.',
43498             cls: 'x-html-editor-tip'
43499         },
43500         decreasefontsize : {
43501             title: 'Shrink Text',
43502             text: 'Decrease the font size.',
43503             cls: 'x-html-editor-tip'
43504         },
43505         backcolor : {
43506             title: 'Text Highlight Color',
43507             text: 'Change the background color of the selected text.',
43508             cls: 'x-html-editor-tip'
43509         },
43510         forecolor : {
43511             title: 'Font Color',
43512             text: 'Change the color of the selected text.',
43513             cls: 'x-html-editor-tip'
43514         },
43515         justifyleft : {
43516             title: 'Align Text Left',
43517             text: 'Align text to the left.',
43518             cls: 'x-html-editor-tip'
43519         },
43520         justifycenter : {
43521             title: 'Center Text',
43522             text: 'Center text in the editor.',
43523             cls: 'x-html-editor-tip'
43524         },
43525         justifyright : {
43526             title: 'Align Text Right',
43527             text: 'Align text to the right.',
43528             cls: 'x-html-editor-tip'
43529         },
43530         insertunorderedlist : {
43531             title: 'Bullet List',
43532             text: 'Start a bulleted list.',
43533             cls: 'x-html-editor-tip'
43534         },
43535         insertorderedlist : {
43536             title: 'Numbered List',
43537             text: 'Start a numbered list.',
43538             cls: 'x-html-editor-tip'
43539         },
43540         createlink : {
43541             title: 'Hyperlink',
43542             text: 'Make the selected text a hyperlink.',
43543             cls: 'x-html-editor-tip'
43544         },
43545         sourceedit : {
43546             title: 'Source Edit',
43547             text: 'Switch to source editing mode.',
43548             cls: 'x-html-editor-tip'
43549         }
43550     },
43551     // private
43552     onDestroy : function(){
43553         if(this.rendered){
43554             
43555             this.tb.items.each(function(item){
43556                 if(item.menu){
43557                     item.menu.removeAll();
43558                     if(item.menu.el){
43559                         item.menu.el.destroy();
43560                     }
43561                 }
43562                 item.destroy();
43563             });
43564              
43565         }
43566     },
43567     onFirstFocus: function() {
43568         this.tb.items.each(function(item){
43569            item.enable();
43570         });
43571     }
43572 });
43573
43574
43575
43576
43577 // <script type="text/javascript">
43578 /*
43579  * Based on
43580  * Ext JS Library 1.1.1
43581  * Copyright(c) 2006-2007, Ext JS, LLC.
43582  *  
43583  
43584  */
43585
43586  
43587 /**
43588  * @class Roo.form.HtmlEditor.ToolbarContext
43589  * Context Toolbar
43590  * 
43591  * Usage:
43592  *
43593  new Roo.form.HtmlEditor({
43594     ....
43595     toolbars : [
43596         { xtype: 'ToolbarStandard', styles : {} }
43597         { xtype: 'ToolbarContext', disable : {} }
43598     ]
43599 })
43600
43601      
43602  * 
43603  * @config : {Object} disable List of elements to disable.. (not done yet.)
43604  * @config : {Object} styles  Map of styles available.
43605  * 
43606  */
43607
43608 Roo.form.HtmlEditor.ToolbarContext = function(config)
43609 {
43610     
43611     Roo.apply(this, config);
43612     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43613     // dont call parent... till later.
43614     this.styles = this.styles || {};
43615 }
43616
43617  
43618
43619 Roo.form.HtmlEditor.ToolbarContext.types = {
43620     'IMG' : {
43621         width : {
43622             title: "Width",
43623             width: 40
43624         },
43625         height:  {
43626             title: "Height",
43627             width: 40
43628         },
43629         align: {
43630             title: "Align",
43631             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43632             width : 80
43633             
43634         },
43635         border: {
43636             title: "Border",
43637             width: 40
43638         },
43639         alt: {
43640             title: "Alt",
43641             width: 120
43642         },
43643         src : {
43644             title: "Src",
43645             width: 220
43646         }
43647         
43648     },
43649     'A' : {
43650         name : {
43651             title: "Name",
43652             width: 50
43653         },
43654         target:  {
43655             title: "Target",
43656             width: 120
43657         },
43658         href:  {
43659             title: "Href",
43660             width: 220
43661         } // border?
43662         
43663     },
43664     'TABLE' : {
43665         rows : {
43666             title: "Rows",
43667             width: 20
43668         },
43669         cols : {
43670             title: "Cols",
43671             width: 20
43672         },
43673         width : {
43674             title: "Width",
43675             width: 40
43676         },
43677         height : {
43678             title: "Height",
43679             width: 40
43680         },
43681         border : {
43682             title: "Border",
43683             width: 20
43684         }
43685     },
43686     'TD' : {
43687         width : {
43688             title: "Width",
43689             width: 40
43690         },
43691         height : {
43692             title: "Height",
43693             width: 40
43694         },   
43695         align: {
43696             title: "Align",
43697             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43698             width: 80
43699         },
43700         valign: {
43701             title: "Valign",
43702             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43703             width: 80
43704         },
43705         colspan: {
43706             title: "Colspan",
43707             width: 20
43708             
43709         },
43710          'font-family'  : {
43711             title : "Font",
43712             style : 'fontFamily',
43713             displayField: 'display',
43714             optname : 'font-family',
43715             width: 140
43716         }
43717     },
43718     'INPUT' : {
43719         name : {
43720             title: "name",
43721             width: 120
43722         },
43723         value : {
43724             title: "Value",
43725             width: 120
43726         },
43727         width : {
43728             title: "Width",
43729             width: 40
43730         }
43731     },
43732     'LABEL' : {
43733         'for' : {
43734             title: "For",
43735             width: 120
43736         }
43737     },
43738     'TEXTAREA' : {
43739           name : {
43740             title: "name",
43741             width: 120
43742         },
43743         rows : {
43744             title: "Rows",
43745             width: 20
43746         },
43747         cols : {
43748             title: "Cols",
43749             width: 20
43750         }
43751     },
43752     'SELECT' : {
43753         name : {
43754             title: "name",
43755             width: 120
43756         },
43757         selectoptions : {
43758             title: "Options",
43759             width: 200
43760         }
43761     },
43762     
43763     // should we really allow this??
43764     // should this just be 
43765     'BODY' : {
43766         title : {
43767             title: "Title",
43768             width: 200,
43769             disabled : true
43770         }
43771     },
43772     'SPAN' : {
43773         'font-family'  : {
43774             title : "Font",
43775             style : 'fontFamily',
43776             displayField: 'display',
43777             optname : 'font-family',
43778             width: 140
43779         }
43780     },
43781     'DIV' : {
43782         'font-family'  : {
43783             title : "Font",
43784             style : 'fontFamily',
43785             displayField: 'display',
43786             optname : 'font-family',
43787             width: 140
43788         }
43789     },
43790      'P' : {
43791         'font-family'  : {
43792             title : "Font",
43793             style : 'fontFamily',
43794             displayField: 'display',
43795             optname : 'font-family',
43796             width: 140
43797         }
43798     },
43799     
43800     '*' : {
43801         // empty..
43802     }
43803
43804 };
43805
43806 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43807 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43808
43809 Roo.form.HtmlEditor.ToolbarContext.options = {
43810         'font-family'  : [ 
43811                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43812                 [ 'Courier New', 'Courier New'],
43813                 [ 'Tahoma', 'Tahoma'],
43814                 [ 'Times New Roman,serif', 'Times'],
43815                 [ 'Verdana','Verdana' ]
43816         ]
43817 };
43818
43819 // fixme - these need to be configurable..
43820  
43821
43822 Roo.form.HtmlEditor.ToolbarContext.types
43823
43824
43825 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43826     
43827     tb: false,
43828     
43829     rendered: false,
43830     
43831     editor : false,
43832     editorcore : false,
43833     /**
43834      * @cfg {Object} disable  List of toolbar elements to disable
43835          
43836      */
43837     disable : false,
43838     /**
43839      * @cfg {Object} styles List of styles 
43840      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43841      *
43842      * These must be defined in the page, so they get rendered correctly..
43843      * .headline { }
43844      * TD.underline { }
43845      * 
43846      */
43847     styles : false,
43848     
43849     options: false,
43850     
43851     toolbars : false,
43852     
43853     init : function(editor)
43854     {
43855         this.editor = editor;
43856         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43857         var editorcore = this.editorcore;
43858         
43859         var fid = editorcore.frameId;
43860         var etb = this;
43861         function btn(id, toggle, handler){
43862             var xid = fid + '-'+ id ;
43863             return {
43864                 id : xid,
43865                 cmd : id,
43866                 cls : 'x-btn-icon x-edit-'+id,
43867                 enableToggle:toggle !== false,
43868                 scope: editorcore, // was editor...
43869                 handler:handler||editorcore.relayBtnCmd,
43870                 clickEvent:'mousedown',
43871                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43872                 tabIndex:-1
43873             };
43874         }
43875         // create a new element.
43876         var wdiv = editor.wrap.createChild({
43877                 tag: 'div'
43878             }, editor.wrap.dom.firstChild.nextSibling, true);
43879         
43880         // can we do this more than once??
43881         
43882          // stop form submits
43883       
43884  
43885         // disable everything...
43886         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43887         this.toolbars = {};
43888            
43889         for (var i in  ty) {
43890           
43891             this.toolbars[i] = this.buildToolbar(ty[i],i);
43892         }
43893         this.tb = this.toolbars.BODY;
43894         this.tb.el.show();
43895         this.buildFooter();
43896         this.footer.show();
43897         editor.on('hide', function( ) { this.footer.hide() }, this);
43898         editor.on('show', function( ) { this.footer.show() }, this);
43899         
43900          
43901         this.rendered = true;
43902         
43903         // the all the btns;
43904         editor.on('editorevent', this.updateToolbar, this);
43905         // other toolbars need to implement this..
43906         //editor.on('editmodechange', this.updateToolbar, this);
43907     },
43908     
43909     
43910     
43911     /**
43912      * Protected method that will not generally be called directly. It triggers
43913      * a toolbar update by reading the markup state of the current selection in the editor.
43914      */
43915     updateToolbar: function(editor,ev,sel){
43916
43917         //Roo.log(ev);
43918         // capture mouse up - this is handy for selecting images..
43919         // perhaps should go somewhere else...
43920         if(!this.editorcore.activated){
43921              this.editor.onFirstFocus();
43922             return;
43923         }
43924         
43925         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43926         // selectNode - might want to handle IE?
43927         if (ev &&
43928             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43929             ev.target && ev.target.tagName == 'IMG') {
43930             // they have click on an image...
43931             // let's see if we can change the selection...
43932             sel = ev.target;
43933          
43934               var nodeRange = sel.ownerDocument.createRange();
43935             try {
43936                 nodeRange.selectNode(sel);
43937             } catch (e) {
43938                 nodeRange.selectNodeContents(sel);
43939             }
43940             //nodeRange.collapse(true);
43941             var s = this.editorcore.win.getSelection();
43942             s.removeAllRanges();
43943             s.addRange(nodeRange);
43944         }  
43945         
43946       
43947         var updateFooter = sel ? false : true;
43948         
43949         
43950         var ans = this.editorcore.getAllAncestors();
43951         
43952         // pick
43953         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43954         
43955         if (!sel) { 
43956             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43957             sel = sel ? sel : this.editorcore.doc.body;
43958             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43959             
43960         }
43961         // pick a menu that exists..
43962         var tn = sel.tagName.toUpperCase();
43963         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43964         
43965         tn = sel.tagName.toUpperCase();
43966         
43967         var lastSel = this.tb.selectedNode
43968         
43969         this.tb.selectedNode = sel;
43970         
43971         // if current menu does not match..
43972         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43973                 
43974             this.tb.el.hide();
43975             ///console.log("show: " + tn);
43976             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43977             this.tb.el.show();
43978             // update name
43979             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43980             
43981             
43982             // update attributes
43983             if (this.tb.fields) {
43984                 this.tb.fields.each(function(e) {
43985                     if (e.stylename) {
43986                         e.setValue(sel.style[e.stylename]);
43987                         return;
43988                     } 
43989                    e.setValue(sel.getAttribute(e.attrname));
43990                 });
43991             }
43992             
43993             var hasStyles = false;
43994             for(var i in this.styles) {
43995                 hasStyles = true;
43996                 break;
43997             }
43998             
43999             // update styles
44000             if (hasStyles) { 
44001                 var st = this.tb.fields.item(0);
44002                 
44003                 st.store.removeAll();
44004                
44005                 
44006                 var cn = sel.className.split(/\s+/);
44007                 
44008                 var avs = [];
44009                 if (this.styles['*']) {
44010                     
44011                     Roo.each(this.styles['*'], function(v) {
44012                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44013                     });
44014                 }
44015                 if (this.styles[tn]) { 
44016                     Roo.each(this.styles[tn], function(v) {
44017                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44018                     });
44019                 }
44020                 
44021                 st.store.loadData(avs);
44022                 st.collapse();
44023                 st.setValue(cn);
44024             }
44025             // flag our selected Node.
44026             this.tb.selectedNode = sel;
44027            
44028            
44029             Roo.menu.MenuMgr.hideAll();
44030
44031         }
44032         
44033         if (!updateFooter) {
44034             //this.footDisp.dom.innerHTML = ''; 
44035             return;
44036         }
44037         // update the footer
44038         //
44039         var html = '';
44040         
44041         this.footerEls = ans.reverse();
44042         Roo.each(this.footerEls, function(a,i) {
44043             if (!a) { return; }
44044             html += html.length ? ' &gt; '  :  '';
44045             
44046             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44047             
44048         });
44049        
44050         // 
44051         var sz = this.footDisp.up('td').getSize();
44052         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44053         this.footDisp.dom.style.marginLeft = '5px';
44054         
44055         this.footDisp.dom.style.overflow = 'hidden';
44056         
44057         this.footDisp.dom.innerHTML = html;
44058             
44059         //this.editorsyncValue();
44060     },
44061      
44062     
44063    
44064        
44065     // private
44066     onDestroy : function(){
44067         if(this.rendered){
44068             
44069             this.tb.items.each(function(item){
44070                 if(item.menu){
44071                     item.menu.removeAll();
44072                     if(item.menu.el){
44073                         item.menu.el.destroy();
44074                     }
44075                 }
44076                 item.destroy();
44077             });
44078              
44079         }
44080     },
44081     onFirstFocus: function() {
44082         // need to do this for all the toolbars..
44083         this.tb.items.each(function(item){
44084            item.enable();
44085         });
44086     },
44087     buildToolbar: function(tlist, nm)
44088     {
44089         var editor = this.editor;
44090         var editorcore = this.editorcore;
44091          // create a new element.
44092         var wdiv = editor.wrap.createChild({
44093                 tag: 'div'
44094             }, editor.wrap.dom.firstChild.nextSibling, true);
44095         
44096        
44097         var tb = new Roo.Toolbar(wdiv);
44098         // add the name..
44099         
44100         tb.add(nm+ ":&nbsp;");
44101         
44102         var styles = [];
44103         for(var i in this.styles) {
44104             styles.push(i);
44105         }
44106         
44107         // styles...
44108         if (styles && styles.length) {
44109             
44110             // this needs a multi-select checkbox...
44111             tb.addField( new Roo.form.ComboBox({
44112                 store: new Roo.data.SimpleStore({
44113                     id : 'val',
44114                     fields: ['val', 'selected'],
44115                     data : [] 
44116                 }),
44117                 name : '-roo-edit-className',
44118                 attrname : 'className',
44119                 displayField: 'val',
44120                 typeAhead: false,
44121                 mode: 'local',
44122                 editable : false,
44123                 triggerAction: 'all',
44124                 emptyText:'Select Style',
44125                 selectOnFocus:true,
44126                 width: 130,
44127                 listeners : {
44128                     'select': function(c, r, i) {
44129                         // initial support only for on class per el..
44130                         tb.selectedNode.className =  r ? r.get('val') : '';
44131                         editorcore.syncValue();
44132                     }
44133                 }
44134     
44135             }));
44136         }
44137         
44138         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44139         var tbops = tbc.options;
44140         
44141         for (var i in tlist) {
44142             
44143             var item = tlist[i];
44144             tb.add(item.title + ":&nbsp;");
44145             
44146             
44147             //optname == used so you can configure the options available..
44148             var opts = item.opts ? item.opts : false;
44149             if (item.optname) {
44150                 opts = tbops[item.optname];
44151            
44152             }
44153             
44154             if (opts) {
44155                 // opts == pulldown..
44156                 tb.addField( new Roo.form.ComboBox({
44157                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44158                         id : 'val',
44159                         fields: ['val', 'display'],
44160                         data : opts  
44161                     }),
44162                     name : '-roo-edit-' + i,
44163                     attrname : i,
44164                     stylename : item.style ? item.style : false,
44165                     displayField: item.displayField ? item.displayField : 'val',
44166                     valueField :  'val',
44167                     typeAhead: false,
44168                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44169                     editable : false,
44170                     triggerAction: 'all',
44171                     emptyText:'Select',
44172                     selectOnFocus:true,
44173                     width: item.width ? item.width  : 130,
44174                     listeners : {
44175                         'select': function(c, r, i) {
44176                             if (c.stylename) {
44177                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44178                                 return;
44179                             }
44180                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44181                         }
44182                     }
44183
44184                 }));
44185                 continue;
44186                     
44187                  
44188                 
44189                 tb.addField( new Roo.form.TextField({
44190                     name: i,
44191                     width: 100,
44192                     //allowBlank:false,
44193                     value: ''
44194                 }));
44195                 continue;
44196             }
44197             tb.addField( new Roo.form.TextField({
44198                 name: '-roo-edit-' + i,
44199                 attrname : i,
44200                 
44201                 width: item.width,
44202                 //allowBlank:true,
44203                 value: '',
44204                 listeners: {
44205                     'change' : function(f, nv, ov) {
44206                         tb.selectedNode.setAttribute(f.attrname, nv);
44207                     }
44208                 }
44209             }));
44210              
44211         }
44212         tb.addFill();
44213         var _this = this;
44214         tb.addButton( {
44215             text: 'Remove Tag',
44216     
44217             listeners : {
44218                 click : function ()
44219                 {
44220                     // remove
44221                     // undo does not work.
44222                      
44223                     var sn = tb.selectedNode;
44224                     
44225                     var pn = sn.parentNode;
44226                     
44227                     var stn =  sn.childNodes[0];
44228                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44229                     while (sn.childNodes.length) {
44230                         var node = sn.childNodes[0];
44231                         sn.removeChild(node);
44232                         //Roo.log(node);
44233                         pn.insertBefore(node, sn);
44234                         
44235                     }
44236                     pn.removeChild(sn);
44237                     var range = editorcore.createRange();
44238         
44239                     range.setStart(stn,0);
44240                     range.setEnd(en,0); //????
44241                     //range.selectNode(sel);
44242                     
44243                     
44244                     var selection = editorcore.getSelection();
44245                     selection.removeAllRanges();
44246                     selection.addRange(range);
44247                     
44248                     
44249                     
44250                     //_this.updateToolbar(null, null, pn);
44251                     _this.updateToolbar(null, null, null);
44252                     _this.footDisp.dom.innerHTML = ''; 
44253                 }
44254             }
44255             
44256                     
44257                 
44258             
44259         });
44260         
44261         
44262         tb.el.on('click', function(e){
44263             e.preventDefault(); // what does this do?
44264         });
44265         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44266         tb.el.hide();
44267         tb.name = nm;
44268         // dont need to disable them... as they will get hidden
44269         return tb;
44270          
44271         
44272     },
44273     buildFooter : function()
44274     {
44275         
44276         var fel = this.editor.wrap.createChild();
44277         this.footer = new Roo.Toolbar(fel);
44278         // toolbar has scrolly on left / right?
44279         var footDisp= new Roo.Toolbar.Fill();
44280         var _t = this;
44281         this.footer.add(
44282             {
44283                 text : '&lt;',
44284                 xtype: 'Button',
44285                 handler : function() {
44286                     _t.footDisp.scrollTo('left',0,true)
44287                 }
44288             }
44289         );
44290         this.footer.add( footDisp );
44291         this.footer.add( 
44292             {
44293                 text : '&gt;',
44294                 xtype: 'Button',
44295                 handler : function() {
44296                     // no animation..
44297                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44298                 }
44299             }
44300         );
44301         var fel = Roo.get(footDisp.el);
44302         fel.addClass('x-editor-context');
44303         this.footDispWrap = fel; 
44304         this.footDispWrap.overflow  = 'hidden';
44305         
44306         this.footDisp = fel.createChild();
44307         this.footDispWrap.on('click', this.onContextClick, this)
44308         
44309         
44310     },
44311     onContextClick : function (ev,dom)
44312     {
44313         ev.preventDefault();
44314         var  cn = dom.className;
44315         //Roo.log(cn);
44316         if (!cn.match(/x-ed-loc-/)) {
44317             return;
44318         }
44319         var n = cn.split('-').pop();
44320         var ans = this.footerEls;
44321         var sel = ans[n];
44322         
44323          // pick
44324         var range = this.editorcore.createRange();
44325         
44326         range.selectNodeContents(sel);
44327         //range.selectNode(sel);
44328         
44329         
44330         var selection = this.editorcore.getSelection();
44331         selection.removeAllRanges();
44332         selection.addRange(range);
44333         
44334         
44335         
44336         this.updateToolbar(null, null, sel);
44337         
44338         
44339     }
44340     
44341     
44342     
44343     
44344     
44345 });
44346
44347
44348
44349
44350
44351 /*
44352  * Based on:
44353  * Ext JS Library 1.1.1
44354  * Copyright(c) 2006-2007, Ext JS, LLC.
44355  *
44356  * Originally Released Under LGPL - original licence link has changed is not relivant.
44357  *
44358  * Fork - LGPL
44359  * <script type="text/javascript">
44360  */
44361  
44362 /**
44363  * @class Roo.form.BasicForm
44364  * @extends Roo.util.Observable
44365  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44366  * @constructor
44367  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44368  * @param {Object} config Configuration options
44369  */
44370 Roo.form.BasicForm = function(el, config){
44371     this.allItems = [];
44372     this.childForms = [];
44373     Roo.apply(this, config);
44374     /*
44375      * The Roo.form.Field items in this form.
44376      * @type MixedCollection
44377      */
44378      
44379      
44380     this.items = new Roo.util.MixedCollection(false, function(o){
44381         return o.id || (o.id = Roo.id());
44382     });
44383     this.addEvents({
44384         /**
44385          * @event beforeaction
44386          * Fires before any action is performed. Return false to cancel the action.
44387          * @param {Form} this
44388          * @param {Action} action The action to be performed
44389          */
44390         beforeaction: true,
44391         /**
44392          * @event actionfailed
44393          * Fires when an action fails.
44394          * @param {Form} this
44395          * @param {Action} action The action that failed
44396          */
44397         actionfailed : true,
44398         /**
44399          * @event actioncomplete
44400          * Fires when an action is completed.
44401          * @param {Form} this
44402          * @param {Action} action The action that completed
44403          */
44404         actioncomplete : true
44405     });
44406     if(el){
44407         this.initEl(el);
44408     }
44409     Roo.form.BasicForm.superclass.constructor.call(this);
44410 };
44411
44412 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44413     /**
44414      * @cfg {String} method
44415      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44416      */
44417     /**
44418      * @cfg {DataReader} reader
44419      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44420      * This is optional as there is built-in support for processing JSON.
44421      */
44422     /**
44423      * @cfg {DataReader} errorReader
44424      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44425      * This is completely optional as there is built-in support for processing JSON.
44426      */
44427     /**
44428      * @cfg {String} url
44429      * The URL to use for form actions if one isn't supplied in the action options.
44430      */
44431     /**
44432      * @cfg {Boolean} fileUpload
44433      * Set to true if this form is a file upload.
44434      */
44435      
44436     /**
44437      * @cfg {Object} baseParams
44438      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44439      */
44440      /**
44441      
44442     /**
44443      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44444      */
44445     timeout: 30,
44446
44447     // private
44448     activeAction : null,
44449
44450     /**
44451      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44452      * or setValues() data instead of when the form was first created.
44453      */
44454     trackResetOnLoad : false,
44455     
44456     
44457     /**
44458      * childForms - used for multi-tab forms
44459      * @type {Array}
44460      */
44461     childForms : false,
44462     
44463     /**
44464      * allItems - full list of fields.
44465      * @type {Array}
44466      */
44467     allItems : false,
44468     
44469     /**
44470      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44471      * element by passing it or its id or mask the form itself by passing in true.
44472      * @type Mixed
44473      */
44474     waitMsgTarget : false,
44475
44476     // private
44477     initEl : function(el){
44478         this.el = Roo.get(el);
44479         this.id = this.el.id || Roo.id();
44480         this.el.on('submit', this.onSubmit, this);
44481         this.el.addClass('x-form');
44482     },
44483
44484     // private
44485     onSubmit : function(e){
44486         e.stopEvent();
44487     },
44488
44489     /**
44490      * Returns true if client-side validation on the form is successful.
44491      * @return Boolean
44492      */
44493     isValid : function(){
44494         var valid = true;
44495         this.items.each(function(f){
44496            if(!f.validate()){
44497                valid = false;
44498            }
44499         });
44500         return valid;
44501     },
44502
44503     /**
44504      * Returns true if any fields in this form have changed since their original load.
44505      * @return Boolean
44506      */
44507     isDirty : function(){
44508         var dirty = false;
44509         this.items.each(function(f){
44510            if(f.isDirty()){
44511                dirty = true;
44512                return false;
44513            }
44514         });
44515         return dirty;
44516     },
44517
44518     /**
44519      * Performs a predefined action (submit or load) or custom actions you define on this form.
44520      * @param {String} actionName The name of the action type
44521      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44522      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44523      * accept other config options):
44524      * <pre>
44525 Property          Type             Description
44526 ----------------  ---------------  ----------------------------------------------------------------------------------
44527 url               String           The url for the action (defaults to the form's url)
44528 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44529 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44530 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44531                                    validate the form on the client (defaults to false)
44532      * </pre>
44533      * @return {BasicForm} this
44534      */
44535     doAction : function(action, options){
44536         if(typeof action == 'string'){
44537             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44538         }
44539         if(this.fireEvent('beforeaction', this, action) !== false){
44540             this.beforeAction(action);
44541             action.run.defer(100, action);
44542         }
44543         return this;
44544     },
44545
44546     /**
44547      * Shortcut to do a submit action.
44548      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44549      * @return {BasicForm} this
44550      */
44551     submit : function(options){
44552         this.doAction('submit', options);
44553         return this;
44554     },
44555
44556     /**
44557      * Shortcut to do a load action.
44558      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44559      * @return {BasicForm} this
44560      */
44561     load : function(options){
44562         this.doAction('load', options);
44563         return this;
44564     },
44565
44566     /**
44567      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44568      * @param {Record} record The record to edit
44569      * @return {BasicForm} this
44570      */
44571     updateRecord : function(record){
44572         record.beginEdit();
44573         var fs = record.fields;
44574         fs.each(function(f){
44575             var field = this.findField(f.name);
44576             if(field){
44577                 record.set(f.name, field.getValue());
44578             }
44579         }, this);
44580         record.endEdit();
44581         return this;
44582     },
44583
44584     /**
44585      * Loads an Roo.data.Record into this form.
44586      * @param {Record} record The record to load
44587      * @return {BasicForm} this
44588      */
44589     loadRecord : function(record){
44590         this.setValues(record.data);
44591         return this;
44592     },
44593
44594     // private
44595     beforeAction : function(action){
44596         var o = action.options;
44597         
44598        
44599         if(this.waitMsgTarget === true){
44600             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44601         }else if(this.waitMsgTarget){
44602             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44603             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44604         }else {
44605             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44606         }
44607          
44608     },
44609
44610     // private
44611     afterAction : function(action, success){
44612         this.activeAction = null;
44613         var o = action.options;
44614         
44615         if(this.waitMsgTarget === true){
44616             this.el.unmask();
44617         }else if(this.waitMsgTarget){
44618             this.waitMsgTarget.unmask();
44619         }else{
44620             Roo.MessageBox.updateProgress(1);
44621             Roo.MessageBox.hide();
44622         }
44623          
44624         if(success){
44625             if(o.reset){
44626                 this.reset();
44627             }
44628             Roo.callback(o.success, o.scope, [this, action]);
44629             this.fireEvent('actioncomplete', this, action);
44630             
44631         }else{
44632             
44633             // failure condition..
44634             // we have a scenario where updates need confirming.
44635             // eg. if a locking scenario exists..
44636             // we look for { errors : { needs_confirm : true }} in the response.
44637             if (
44638                 (typeof(action.result) != 'undefined')  &&
44639                 (typeof(action.result.errors) != 'undefined')  &&
44640                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44641            ){
44642                 var _t = this;
44643                 Roo.MessageBox.confirm(
44644                     "Change requires confirmation",
44645                     action.result.errorMsg,
44646                     function(r) {
44647                         if (r != 'yes') {
44648                             return;
44649                         }
44650                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44651                     }
44652                     
44653                 );
44654                 
44655                 
44656                 
44657                 return;
44658             }
44659             
44660             Roo.callback(o.failure, o.scope, [this, action]);
44661             // show an error message if no failed handler is set..
44662             if (!this.hasListener('actionfailed')) {
44663                 Roo.MessageBox.alert("Error",
44664                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44665                         action.result.errorMsg :
44666                         "Saving Failed, please check your entries or try again"
44667                 );
44668             }
44669             
44670             this.fireEvent('actionfailed', this, action);
44671         }
44672         
44673     },
44674
44675     /**
44676      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44677      * @param {String} id The value to search for
44678      * @return Field
44679      */
44680     findField : function(id){
44681         var field = this.items.get(id);
44682         if(!field){
44683             this.items.each(function(f){
44684                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44685                     field = f;
44686                     return false;
44687                 }
44688             });
44689         }
44690         return field || null;
44691     },
44692
44693     /**
44694      * Add a secondary form to this one, 
44695      * Used to provide tabbed forms. One form is primary, with hidden values 
44696      * which mirror the elements from the other forms.
44697      * 
44698      * @param {Roo.form.Form} form to add.
44699      * 
44700      */
44701     addForm : function(form)
44702     {
44703        
44704         if (this.childForms.indexOf(form) > -1) {
44705             // already added..
44706             return;
44707         }
44708         this.childForms.push(form);
44709         var n = '';
44710         Roo.each(form.allItems, function (fe) {
44711             
44712             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44713             if (this.findField(n)) { // already added..
44714                 return;
44715             }
44716             var add = new Roo.form.Hidden({
44717                 name : n
44718             });
44719             add.render(this.el);
44720             
44721             this.add( add );
44722         }, this);
44723         
44724     },
44725     /**
44726      * Mark fields in this form invalid in bulk.
44727      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44728      * @return {BasicForm} this
44729      */
44730     markInvalid : function(errors){
44731         if(errors instanceof Array){
44732             for(var i = 0, len = errors.length; i < len; i++){
44733                 var fieldError = errors[i];
44734                 var f = this.findField(fieldError.id);
44735                 if(f){
44736                     f.markInvalid(fieldError.msg);
44737                 }
44738             }
44739         }else{
44740             var field, id;
44741             for(id in errors){
44742                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44743                     field.markInvalid(errors[id]);
44744                 }
44745             }
44746         }
44747         Roo.each(this.childForms || [], function (f) {
44748             f.markInvalid(errors);
44749         });
44750         
44751         return this;
44752     },
44753
44754     /**
44755      * Set values for fields in this form in bulk.
44756      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44757      * @return {BasicForm} this
44758      */
44759     setValues : function(values){
44760         if(values instanceof Array){ // array of objects
44761             for(var i = 0, len = values.length; i < len; i++){
44762                 var v = values[i];
44763                 var f = this.findField(v.id);
44764                 if(f){
44765                     f.setValue(v.value);
44766                     if(this.trackResetOnLoad){
44767                         f.originalValue = f.getValue();
44768                     }
44769                 }
44770             }
44771         }else{ // object hash
44772             var field, id;
44773             for(id in values){
44774                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44775                     
44776                     if (field.setFromData && 
44777                         field.valueField && 
44778                         field.displayField &&
44779                         // combos' with local stores can 
44780                         // be queried via setValue()
44781                         // to set their value..
44782                         (field.store && !field.store.isLocal)
44783                         ) {
44784                         // it's a combo
44785                         var sd = { };
44786                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44787                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44788                         field.setFromData(sd);
44789                         
44790                     } else {
44791                         field.setValue(values[id]);
44792                     }
44793                     
44794                     
44795                     if(this.trackResetOnLoad){
44796                         field.originalValue = field.getValue();
44797                     }
44798                 }
44799             }
44800         }
44801          
44802         Roo.each(this.childForms || [], function (f) {
44803             f.setValues(values);
44804         });
44805                 
44806         return this;
44807     },
44808
44809     /**
44810      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44811      * they are returned as an array.
44812      * @param {Boolean} asString
44813      * @return {Object}
44814      */
44815     getValues : function(asString){
44816         if (this.childForms) {
44817             // copy values from the child forms
44818             Roo.each(this.childForms, function (f) {
44819                 this.setValues(f.getValues());
44820             }, this);
44821         }
44822         
44823         
44824         
44825         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44826         if(asString === true){
44827             return fs;
44828         }
44829         return Roo.urlDecode(fs);
44830     },
44831     
44832     /**
44833      * Returns the fields in this form as an object with key/value pairs. 
44834      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44835      * @return {Object}
44836      */
44837     getFieldValues : function(with_hidden)
44838     {
44839         if (this.childForms) {
44840             // copy values from the child forms
44841             // should this call getFieldValues - probably not as we do not currently copy
44842             // hidden fields when we generate..
44843             Roo.each(this.childForms, function (f) {
44844                 this.setValues(f.getValues());
44845             }, this);
44846         }
44847         
44848         var ret = {};
44849         this.items.each(function(f){
44850             if (!f.getName()) {
44851                 return;
44852             }
44853             var v = f.getValue();
44854             if (f.inputType =='radio') {
44855                 if (typeof(ret[f.getName()]) == 'undefined') {
44856                     ret[f.getName()] = ''; // empty..
44857                 }
44858                 
44859                 if (!f.el.dom.checked) {
44860                     return;
44861                     
44862                 }
44863                 v = f.el.dom.value;
44864                 
44865             }
44866             
44867             // not sure if this supported any more..
44868             if ((typeof(v) == 'object') && f.getRawValue) {
44869                 v = f.getRawValue() ; // dates..
44870             }
44871             // combo boxes where name != hiddenName...
44872             if (f.name != f.getName()) {
44873                 ret[f.name] = f.getRawValue();
44874             }
44875             ret[f.getName()] = v;
44876         });
44877         
44878         return ret;
44879     },
44880
44881     /**
44882      * Clears all invalid messages in this form.
44883      * @return {BasicForm} this
44884      */
44885     clearInvalid : function(){
44886         this.items.each(function(f){
44887            f.clearInvalid();
44888         });
44889         
44890         Roo.each(this.childForms || [], function (f) {
44891             f.clearInvalid();
44892         });
44893         
44894         
44895         return this;
44896     },
44897
44898     /**
44899      * Resets this form.
44900      * @return {BasicForm} this
44901      */
44902     reset : function(){
44903         this.items.each(function(f){
44904             f.reset();
44905         });
44906         
44907         Roo.each(this.childForms || [], function (f) {
44908             f.reset();
44909         });
44910        
44911         
44912         return this;
44913     },
44914
44915     /**
44916      * Add Roo.form components to this form.
44917      * @param {Field} field1
44918      * @param {Field} field2 (optional)
44919      * @param {Field} etc (optional)
44920      * @return {BasicForm} this
44921      */
44922     add : function(){
44923         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44924         return this;
44925     },
44926
44927
44928     /**
44929      * Removes a field from the items collection (does NOT remove its markup).
44930      * @param {Field} field
44931      * @return {BasicForm} this
44932      */
44933     remove : function(field){
44934         this.items.remove(field);
44935         return this;
44936     },
44937
44938     /**
44939      * Looks at the fields in this form, checks them for an id attribute,
44940      * and calls applyTo on the existing dom element with that id.
44941      * @return {BasicForm} this
44942      */
44943     render : function(){
44944         this.items.each(function(f){
44945             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44946                 f.applyTo(f.id);
44947             }
44948         });
44949         return this;
44950     },
44951
44952     /**
44953      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44954      * @param {Object} values
44955      * @return {BasicForm} this
44956      */
44957     applyToFields : function(o){
44958         this.items.each(function(f){
44959            Roo.apply(f, o);
44960         });
44961         return this;
44962     },
44963
44964     /**
44965      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44966      * @param {Object} values
44967      * @return {BasicForm} this
44968      */
44969     applyIfToFields : function(o){
44970         this.items.each(function(f){
44971            Roo.applyIf(f, o);
44972         });
44973         return this;
44974     }
44975 });
44976
44977 // back compat
44978 Roo.BasicForm = Roo.form.BasicForm;/*
44979  * Based on:
44980  * Ext JS Library 1.1.1
44981  * Copyright(c) 2006-2007, Ext JS, LLC.
44982  *
44983  * Originally Released Under LGPL - original licence link has changed is not relivant.
44984  *
44985  * Fork - LGPL
44986  * <script type="text/javascript">
44987  */
44988
44989 /**
44990  * @class Roo.form.Form
44991  * @extends Roo.form.BasicForm
44992  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44993  * @constructor
44994  * @param {Object} config Configuration options
44995  */
44996 Roo.form.Form = function(config){
44997     var xitems =  [];
44998     if (config.items) {
44999         xitems = config.items;
45000         delete config.items;
45001     }
45002    
45003     
45004     Roo.form.Form.superclass.constructor.call(this, null, config);
45005     this.url = this.url || this.action;
45006     if(!this.root){
45007         this.root = new Roo.form.Layout(Roo.applyIf({
45008             id: Roo.id()
45009         }, config));
45010     }
45011     this.active = this.root;
45012     /**
45013      * Array of all the buttons that have been added to this form via {@link addButton}
45014      * @type Array
45015      */
45016     this.buttons = [];
45017     this.allItems = [];
45018     this.addEvents({
45019         /**
45020          * @event clientvalidation
45021          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45022          * @param {Form} this
45023          * @param {Boolean} valid true if the form has passed client-side validation
45024          */
45025         clientvalidation: true,
45026         /**
45027          * @event rendered
45028          * Fires when the form is rendered
45029          * @param {Roo.form.Form} form
45030          */
45031         rendered : true
45032     });
45033     
45034     if (this.progressUrl) {
45035             // push a hidden field onto the list of fields..
45036             this.addxtype( {
45037                     xns: Roo.form, 
45038                     xtype : 'Hidden', 
45039                     name : 'UPLOAD_IDENTIFIER' 
45040             });
45041         }
45042         
45043     
45044     Roo.each(xitems, this.addxtype, this);
45045     
45046     
45047     
45048 };
45049
45050 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45051     /**
45052      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45053      */
45054     /**
45055      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45056      */
45057     /**
45058      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45059      */
45060     buttonAlign:'center',
45061
45062     /**
45063      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45064      */
45065     minButtonWidth:75,
45066
45067     /**
45068      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45069      * This property cascades to child containers if not set.
45070      */
45071     labelAlign:'left',
45072
45073     /**
45074      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45075      * fires a looping event with that state. This is required to bind buttons to the valid
45076      * state using the config value formBind:true on the button.
45077      */
45078     monitorValid : false,
45079
45080     /**
45081      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45082      */
45083     monitorPoll : 200,
45084     
45085     /**
45086      * @cfg {String} progressUrl - Url to return progress data 
45087      */
45088     
45089     progressUrl : false,
45090   
45091     /**
45092      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45093      * fields are added and the column is closed. If no fields are passed the column remains open
45094      * until end() is called.
45095      * @param {Object} config The config to pass to the column
45096      * @param {Field} field1 (optional)
45097      * @param {Field} field2 (optional)
45098      * @param {Field} etc (optional)
45099      * @return Column The column container object
45100      */
45101     column : function(c){
45102         var col = new Roo.form.Column(c);
45103         this.start(col);
45104         if(arguments.length > 1){ // duplicate code required because of Opera
45105             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45106             this.end();
45107         }
45108         return col;
45109     },
45110
45111     /**
45112      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45113      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45114      * until end() is called.
45115      * @param {Object} config The config to pass to the fieldset
45116      * @param {Field} field1 (optional)
45117      * @param {Field} field2 (optional)
45118      * @param {Field} etc (optional)
45119      * @return FieldSet The fieldset container object
45120      */
45121     fieldset : function(c){
45122         var fs = new Roo.form.FieldSet(c);
45123         this.start(fs);
45124         if(arguments.length > 1){ // duplicate code required because of Opera
45125             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45126             this.end();
45127         }
45128         return fs;
45129     },
45130
45131     /**
45132      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45133      * fields are added and the container is closed. If no fields are passed the container remains open
45134      * until end() is called.
45135      * @param {Object} config The config to pass to the Layout
45136      * @param {Field} field1 (optional)
45137      * @param {Field} field2 (optional)
45138      * @param {Field} etc (optional)
45139      * @return Layout The container object
45140      */
45141     container : function(c){
45142         var l = new Roo.form.Layout(c);
45143         this.start(l);
45144         if(arguments.length > 1){ // duplicate code required because of Opera
45145             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45146             this.end();
45147         }
45148         return l;
45149     },
45150
45151     /**
45152      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45153      * @param {Object} container A Roo.form.Layout or subclass of Layout
45154      * @return {Form} this
45155      */
45156     start : function(c){
45157         // cascade label info
45158         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45159         this.active.stack.push(c);
45160         c.ownerCt = this.active;
45161         this.active = c;
45162         return this;
45163     },
45164
45165     /**
45166      * Closes the current open container
45167      * @return {Form} this
45168      */
45169     end : function(){
45170         if(this.active == this.root){
45171             return this;
45172         }
45173         this.active = this.active.ownerCt;
45174         return this;
45175     },
45176
45177     /**
45178      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45179      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45180      * as the label of the field.
45181      * @param {Field} field1
45182      * @param {Field} field2 (optional)
45183      * @param {Field} etc. (optional)
45184      * @return {Form} this
45185      */
45186     add : function(){
45187         this.active.stack.push.apply(this.active.stack, arguments);
45188         this.allItems.push.apply(this.allItems,arguments);
45189         var r = [];
45190         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45191             if(a[i].isFormField){
45192                 r.push(a[i]);
45193             }
45194         }
45195         if(r.length > 0){
45196             Roo.form.Form.superclass.add.apply(this, r);
45197         }
45198         return this;
45199     },
45200     
45201
45202     
45203     
45204     
45205      /**
45206      * Find any element that has been added to a form, using it's ID or name
45207      * This can include framesets, columns etc. along with regular fields..
45208      * @param {String} id - id or name to find.
45209      
45210      * @return {Element} e - or false if nothing found.
45211      */
45212     findbyId : function(id)
45213     {
45214         var ret = false;
45215         if (!id) {
45216             return ret;
45217         }
45218         Roo.each(this.allItems, function(f){
45219             if (f.id == id || f.name == id ){
45220                 ret = f;
45221                 return false;
45222             }
45223         });
45224         return ret;
45225     },
45226
45227     
45228     
45229     /**
45230      * Render this form into the passed container. This should only be called once!
45231      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45232      * @return {Form} this
45233      */
45234     render : function(ct)
45235     {
45236         
45237         
45238         
45239         ct = Roo.get(ct);
45240         var o = this.autoCreate || {
45241             tag: 'form',
45242             method : this.method || 'POST',
45243             id : this.id || Roo.id()
45244         };
45245         this.initEl(ct.createChild(o));
45246
45247         this.root.render(this.el);
45248         
45249        
45250              
45251         this.items.each(function(f){
45252             f.render('x-form-el-'+f.id);
45253         });
45254
45255         if(this.buttons.length > 0){
45256             // tables are required to maintain order and for correct IE layout
45257             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45258                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45259                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45260             }}, null, true);
45261             var tr = tb.getElementsByTagName('tr')[0];
45262             for(var i = 0, len = this.buttons.length; i < len; i++) {
45263                 var b = this.buttons[i];
45264                 var td = document.createElement('td');
45265                 td.className = 'x-form-btn-td';
45266                 b.render(tr.appendChild(td));
45267             }
45268         }
45269         if(this.monitorValid){ // initialize after render
45270             this.startMonitoring();
45271         }
45272         this.fireEvent('rendered', this);
45273         return this;
45274     },
45275
45276     /**
45277      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45278      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45279      * object or a valid Roo.DomHelper element config
45280      * @param {Function} handler The function called when the button is clicked
45281      * @param {Object} scope (optional) The scope of the handler function
45282      * @return {Roo.Button}
45283      */
45284     addButton : function(config, handler, scope){
45285         var bc = {
45286             handler: handler,
45287             scope: scope,
45288             minWidth: this.minButtonWidth,
45289             hideParent:true
45290         };
45291         if(typeof config == "string"){
45292             bc.text = config;
45293         }else{
45294             Roo.apply(bc, config);
45295         }
45296         var btn = new Roo.Button(null, bc);
45297         this.buttons.push(btn);
45298         return btn;
45299     },
45300
45301      /**
45302      * Adds a series of form elements (using the xtype property as the factory method.
45303      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45304      * @param {Object} config 
45305      */
45306     
45307     addxtype : function()
45308     {
45309         var ar = Array.prototype.slice.call(arguments, 0);
45310         var ret = false;
45311         for(var i = 0; i < ar.length; i++) {
45312             if (!ar[i]) {
45313                 continue; // skip -- if this happends something invalid got sent, we 
45314                 // should ignore it, as basically that interface element will not show up
45315                 // and that should be pretty obvious!!
45316             }
45317             
45318             if (Roo.form[ar[i].xtype]) {
45319                 ar[i].form = this;
45320                 var fe = Roo.factory(ar[i], Roo.form);
45321                 if (!ret) {
45322                     ret = fe;
45323                 }
45324                 fe.form = this;
45325                 if (fe.store) {
45326                     fe.store.form = this;
45327                 }
45328                 if (fe.isLayout) {  
45329                          
45330                     this.start(fe);
45331                     this.allItems.push(fe);
45332                     if (fe.items && fe.addxtype) {
45333                         fe.addxtype.apply(fe, fe.items);
45334                         delete fe.items;
45335                     }
45336                      this.end();
45337                     continue;
45338                 }
45339                 
45340                 
45341                  
45342                 this.add(fe);
45343               //  console.log('adding ' + ar[i].xtype);
45344             }
45345             if (ar[i].xtype == 'Button') {  
45346                 //console.log('adding button');
45347                 //console.log(ar[i]);
45348                 this.addButton(ar[i]);
45349                 this.allItems.push(fe);
45350                 continue;
45351             }
45352             
45353             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45354                 alert('end is not supported on xtype any more, use items');
45355             //    this.end();
45356             //    //console.log('adding end');
45357             }
45358             
45359         }
45360         return ret;
45361     },
45362     
45363     /**
45364      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45365      * option "monitorValid"
45366      */
45367     startMonitoring : function(){
45368         if(!this.bound){
45369             this.bound = true;
45370             Roo.TaskMgr.start({
45371                 run : this.bindHandler,
45372                 interval : this.monitorPoll || 200,
45373                 scope: this
45374             });
45375         }
45376     },
45377
45378     /**
45379      * Stops monitoring of the valid state of this form
45380      */
45381     stopMonitoring : function(){
45382         this.bound = false;
45383     },
45384
45385     // private
45386     bindHandler : function(){
45387         if(!this.bound){
45388             return false; // stops binding
45389         }
45390         var valid = true;
45391         this.items.each(function(f){
45392             if(!f.isValid(true)){
45393                 valid = false;
45394                 return false;
45395             }
45396         });
45397         for(var i = 0, len = this.buttons.length; i < len; i++){
45398             var btn = this.buttons[i];
45399             if(btn.formBind === true && btn.disabled === valid){
45400                 btn.setDisabled(!valid);
45401             }
45402         }
45403         this.fireEvent('clientvalidation', this, valid);
45404     }
45405     
45406     
45407     
45408     
45409     
45410     
45411     
45412     
45413 });
45414
45415
45416 // back compat
45417 Roo.Form = Roo.form.Form;
45418 /*
45419  * Based on:
45420  * Ext JS Library 1.1.1
45421  * Copyright(c) 2006-2007, Ext JS, LLC.
45422  *
45423  * Originally Released Under LGPL - original licence link has changed is not relivant.
45424  *
45425  * Fork - LGPL
45426  * <script type="text/javascript">
45427  */
45428
45429 // as we use this in bootstrap.
45430 Roo.namespace('Roo.form');
45431  /**
45432  * @class Roo.form.Action
45433  * Internal Class used to handle form actions
45434  * @constructor
45435  * @param {Roo.form.BasicForm} el The form element or its id
45436  * @param {Object} config Configuration options
45437  */
45438
45439  
45440  
45441 // define the action interface
45442 Roo.form.Action = function(form, options){
45443     this.form = form;
45444     this.options = options || {};
45445 };
45446 /**
45447  * Client Validation Failed
45448  * @const 
45449  */
45450 Roo.form.Action.CLIENT_INVALID = 'client';
45451 /**
45452  * Server Validation Failed
45453  * @const 
45454  */
45455 Roo.form.Action.SERVER_INVALID = 'server';
45456  /**
45457  * Connect to Server Failed
45458  * @const 
45459  */
45460 Roo.form.Action.CONNECT_FAILURE = 'connect';
45461 /**
45462  * Reading Data from Server Failed
45463  * @const 
45464  */
45465 Roo.form.Action.LOAD_FAILURE = 'load';
45466
45467 Roo.form.Action.prototype = {
45468     type : 'default',
45469     failureType : undefined,
45470     response : undefined,
45471     result : undefined,
45472
45473     // interface method
45474     run : function(options){
45475
45476     },
45477
45478     // interface method
45479     success : function(response){
45480
45481     },
45482
45483     // interface method
45484     handleResponse : function(response){
45485
45486     },
45487
45488     // default connection failure
45489     failure : function(response){
45490         
45491         this.response = response;
45492         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45493         this.form.afterAction(this, false);
45494     },
45495
45496     processResponse : function(response){
45497         this.response = response;
45498         if(!response.responseText){
45499             return true;
45500         }
45501         this.result = this.handleResponse(response);
45502         return this.result;
45503     },
45504
45505     // utility functions used internally
45506     getUrl : function(appendParams){
45507         var url = this.options.url || this.form.url || this.form.el.dom.action;
45508         if(appendParams){
45509             var p = this.getParams();
45510             if(p){
45511                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45512             }
45513         }
45514         return url;
45515     },
45516
45517     getMethod : function(){
45518         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45519     },
45520
45521     getParams : function(){
45522         var bp = this.form.baseParams;
45523         var p = this.options.params;
45524         if(p){
45525             if(typeof p == "object"){
45526                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45527             }else if(typeof p == 'string' && bp){
45528                 p += '&' + Roo.urlEncode(bp);
45529             }
45530         }else if(bp){
45531             p = Roo.urlEncode(bp);
45532         }
45533         return p;
45534     },
45535
45536     createCallback : function(){
45537         return {
45538             success: this.success,
45539             failure: this.failure,
45540             scope: this,
45541             timeout: (this.form.timeout*1000),
45542             upload: this.form.fileUpload ? this.success : undefined
45543         };
45544     }
45545 };
45546
45547 Roo.form.Action.Submit = function(form, options){
45548     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45549 };
45550
45551 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45552     type : 'submit',
45553
45554     haveProgress : false,
45555     uploadComplete : false,
45556     
45557     // uploadProgress indicator.
45558     uploadProgress : function()
45559     {
45560         if (!this.form.progressUrl) {
45561             return;
45562         }
45563         
45564         if (!this.haveProgress) {
45565             Roo.MessageBox.progress("Uploading", "Uploading");
45566         }
45567         if (this.uploadComplete) {
45568            Roo.MessageBox.hide();
45569            return;
45570         }
45571         
45572         this.haveProgress = true;
45573    
45574         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45575         
45576         var c = new Roo.data.Connection();
45577         c.request({
45578             url : this.form.progressUrl,
45579             params: {
45580                 id : uid
45581             },
45582             method: 'GET',
45583             success : function(req){
45584                //console.log(data);
45585                 var rdata = false;
45586                 var edata;
45587                 try  {
45588                    rdata = Roo.decode(req.responseText)
45589                 } catch (e) {
45590                     Roo.log("Invalid data from server..");
45591                     Roo.log(edata);
45592                     return;
45593                 }
45594                 if (!rdata || !rdata.success) {
45595                     Roo.log(rdata);
45596                     Roo.MessageBox.alert(Roo.encode(rdata));
45597                     return;
45598                 }
45599                 var data = rdata.data;
45600                 
45601                 if (this.uploadComplete) {
45602                    Roo.MessageBox.hide();
45603                    return;
45604                 }
45605                    
45606                 if (data){
45607                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45608                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45609                     );
45610                 }
45611                 this.uploadProgress.defer(2000,this);
45612             },
45613        
45614             failure: function(data) {
45615                 Roo.log('progress url failed ');
45616                 Roo.log(data);
45617             },
45618             scope : this
45619         });
45620            
45621     },
45622     
45623     
45624     run : function()
45625     {
45626         // run get Values on the form, so it syncs any secondary forms.
45627         this.form.getValues();
45628         
45629         var o = this.options;
45630         var method = this.getMethod();
45631         var isPost = method == 'POST';
45632         if(o.clientValidation === false || this.form.isValid()){
45633             
45634             if (this.form.progressUrl) {
45635                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45636                     (new Date() * 1) + '' + Math.random());
45637                     
45638             } 
45639             
45640             
45641             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45642                 form:this.form.el.dom,
45643                 url:this.getUrl(!isPost),
45644                 method: method,
45645                 params:isPost ? this.getParams() : null,
45646                 isUpload: this.form.fileUpload
45647             }));
45648             
45649             this.uploadProgress();
45650
45651         }else if (o.clientValidation !== false){ // client validation failed
45652             this.failureType = Roo.form.Action.CLIENT_INVALID;
45653             this.form.afterAction(this, false);
45654         }
45655     },
45656
45657     success : function(response)
45658     {
45659         this.uploadComplete= true;
45660         if (this.haveProgress) {
45661             Roo.MessageBox.hide();
45662         }
45663         
45664         
45665         var result = this.processResponse(response);
45666         if(result === true || result.success){
45667             this.form.afterAction(this, true);
45668             return;
45669         }
45670         if(result.errors){
45671             this.form.markInvalid(result.errors);
45672             this.failureType = Roo.form.Action.SERVER_INVALID;
45673         }
45674         this.form.afterAction(this, false);
45675     },
45676     failure : function(response)
45677     {
45678         this.uploadComplete= true;
45679         if (this.haveProgress) {
45680             Roo.MessageBox.hide();
45681         }
45682         
45683         this.response = response;
45684         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45685         this.form.afterAction(this, false);
45686     },
45687     
45688     handleResponse : function(response){
45689         if(this.form.errorReader){
45690             var rs = this.form.errorReader.read(response);
45691             var errors = [];
45692             if(rs.records){
45693                 for(var i = 0, len = rs.records.length; i < len; i++) {
45694                     var r = rs.records[i];
45695                     errors[i] = r.data;
45696                 }
45697             }
45698             if(errors.length < 1){
45699                 errors = null;
45700             }
45701             return {
45702                 success : rs.success,
45703                 errors : errors
45704             };
45705         }
45706         var ret = false;
45707         try {
45708             ret = Roo.decode(response.responseText);
45709         } catch (e) {
45710             ret = {
45711                 success: false,
45712                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45713                 errors : []
45714             };
45715         }
45716         return ret;
45717         
45718     }
45719 });
45720
45721
45722 Roo.form.Action.Load = function(form, options){
45723     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45724     this.reader = this.form.reader;
45725 };
45726
45727 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45728     type : 'load',
45729
45730     run : function(){
45731         
45732         Roo.Ajax.request(Roo.apply(
45733                 this.createCallback(), {
45734                     method:this.getMethod(),
45735                     url:this.getUrl(false),
45736                     params:this.getParams()
45737         }));
45738     },
45739
45740     success : function(response){
45741         
45742         var result = this.processResponse(response);
45743         if(result === true || !result.success || !result.data){
45744             this.failureType = Roo.form.Action.LOAD_FAILURE;
45745             this.form.afterAction(this, false);
45746             return;
45747         }
45748         this.form.clearInvalid();
45749         this.form.setValues(result.data);
45750         this.form.afterAction(this, true);
45751     },
45752
45753     handleResponse : function(response){
45754         if(this.form.reader){
45755             var rs = this.form.reader.read(response);
45756             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45757             return {
45758                 success : rs.success,
45759                 data : data
45760             };
45761         }
45762         return Roo.decode(response.responseText);
45763     }
45764 });
45765
45766 Roo.form.Action.ACTION_TYPES = {
45767     'load' : Roo.form.Action.Load,
45768     'submit' : Roo.form.Action.Submit
45769 };/*
45770  * Based on:
45771  * Ext JS Library 1.1.1
45772  * Copyright(c) 2006-2007, Ext JS, LLC.
45773  *
45774  * Originally Released Under LGPL - original licence link has changed is not relivant.
45775  *
45776  * Fork - LGPL
45777  * <script type="text/javascript">
45778  */
45779  
45780 /**
45781  * @class Roo.form.Layout
45782  * @extends Roo.Component
45783  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45784  * @constructor
45785  * @param {Object} config Configuration options
45786  */
45787 Roo.form.Layout = function(config){
45788     var xitems = [];
45789     if (config.items) {
45790         xitems = config.items;
45791         delete config.items;
45792     }
45793     Roo.form.Layout.superclass.constructor.call(this, config);
45794     this.stack = [];
45795     Roo.each(xitems, this.addxtype, this);
45796      
45797 };
45798
45799 Roo.extend(Roo.form.Layout, Roo.Component, {
45800     /**
45801      * @cfg {String/Object} autoCreate
45802      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45803      */
45804     /**
45805      * @cfg {String/Object/Function} style
45806      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45807      * a function which returns such a specification.
45808      */
45809     /**
45810      * @cfg {String} labelAlign
45811      * Valid values are "left," "top" and "right" (defaults to "left")
45812      */
45813     /**
45814      * @cfg {Number} labelWidth
45815      * Fixed width in pixels of all field labels (defaults to undefined)
45816      */
45817     /**
45818      * @cfg {Boolean} clear
45819      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45820      */
45821     clear : true,
45822     /**
45823      * @cfg {String} labelSeparator
45824      * The separator to use after field labels (defaults to ':')
45825      */
45826     labelSeparator : ':',
45827     /**
45828      * @cfg {Boolean} hideLabels
45829      * True to suppress the display of field labels in this layout (defaults to false)
45830      */
45831     hideLabels : false,
45832
45833     // private
45834     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45835     
45836     isLayout : true,
45837     
45838     // private
45839     onRender : function(ct, position){
45840         if(this.el){ // from markup
45841             this.el = Roo.get(this.el);
45842         }else {  // generate
45843             var cfg = this.getAutoCreate();
45844             this.el = ct.createChild(cfg, position);
45845         }
45846         if(this.style){
45847             this.el.applyStyles(this.style);
45848         }
45849         if(this.labelAlign){
45850             this.el.addClass('x-form-label-'+this.labelAlign);
45851         }
45852         if(this.hideLabels){
45853             this.labelStyle = "display:none";
45854             this.elementStyle = "padding-left:0;";
45855         }else{
45856             if(typeof this.labelWidth == 'number'){
45857                 this.labelStyle = "width:"+this.labelWidth+"px;";
45858                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45859             }
45860             if(this.labelAlign == 'top'){
45861                 this.labelStyle = "width:auto;";
45862                 this.elementStyle = "padding-left:0;";
45863             }
45864         }
45865         var stack = this.stack;
45866         var slen = stack.length;
45867         if(slen > 0){
45868             if(!this.fieldTpl){
45869                 var t = new Roo.Template(
45870                     '<div class="x-form-item {5}">',
45871                         '<label for="{0}" style="{2}">{1}{4}</label>',
45872                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45873                         '</div>',
45874                     '</div><div class="x-form-clear-left"></div>'
45875                 );
45876                 t.disableFormats = true;
45877                 t.compile();
45878                 Roo.form.Layout.prototype.fieldTpl = t;
45879             }
45880             for(var i = 0; i < slen; i++) {
45881                 if(stack[i].isFormField){
45882                     this.renderField(stack[i]);
45883                 }else{
45884                     this.renderComponent(stack[i]);
45885                 }
45886             }
45887         }
45888         if(this.clear){
45889             this.el.createChild({cls:'x-form-clear'});
45890         }
45891     },
45892
45893     // private
45894     renderField : function(f){
45895         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45896                f.id, //0
45897                f.fieldLabel, //1
45898                f.labelStyle||this.labelStyle||'', //2
45899                this.elementStyle||'', //3
45900                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45901                f.itemCls||this.itemCls||''  //5
45902        ], true).getPrevSibling());
45903     },
45904
45905     // private
45906     renderComponent : function(c){
45907         c.render(c.isLayout ? this.el : this.el.createChild());    
45908     },
45909     /**
45910      * Adds a object form elements (using the xtype property as the factory method.)
45911      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45912      * @param {Object} config 
45913      */
45914     addxtype : function(o)
45915     {
45916         // create the lement.
45917         o.form = this.form;
45918         var fe = Roo.factory(o, Roo.form);
45919         this.form.allItems.push(fe);
45920         this.stack.push(fe);
45921         
45922         if (fe.isFormField) {
45923             this.form.items.add(fe);
45924         }
45925          
45926         return fe;
45927     }
45928 });
45929
45930 /**
45931  * @class Roo.form.Column
45932  * @extends Roo.form.Layout
45933  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45934  * @constructor
45935  * @param {Object} config Configuration options
45936  */
45937 Roo.form.Column = function(config){
45938     Roo.form.Column.superclass.constructor.call(this, config);
45939 };
45940
45941 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45942     /**
45943      * @cfg {Number/String} width
45944      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45945      */
45946     /**
45947      * @cfg {String/Object} autoCreate
45948      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45949      */
45950
45951     // private
45952     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45953
45954     // private
45955     onRender : function(ct, position){
45956         Roo.form.Column.superclass.onRender.call(this, ct, position);
45957         if(this.width){
45958             this.el.setWidth(this.width);
45959         }
45960     }
45961 });
45962
45963
45964 /**
45965  * @class Roo.form.Row
45966  * @extends Roo.form.Layout
45967  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45968  * @constructor
45969  * @param {Object} config Configuration options
45970  */
45971
45972  
45973 Roo.form.Row = function(config){
45974     Roo.form.Row.superclass.constructor.call(this, config);
45975 };
45976  
45977 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45978       /**
45979      * @cfg {Number/String} width
45980      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45981      */
45982     /**
45983      * @cfg {Number/String} height
45984      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45985      */
45986     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45987     
45988     padWidth : 20,
45989     // private
45990     onRender : function(ct, position){
45991         //console.log('row render');
45992         if(!this.rowTpl){
45993             var t = new Roo.Template(
45994                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45995                     '<label for="{0}" style="{2}">{1}{4}</label>',
45996                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45997                     '</div>',
45998                 '</div>'
45999             );
46000             t.disableFormats = true;
46001             t.compile();
46002             Roo.form.Layout.prototype.rowTpl = t;
46003         }
46004         this.fieldTpl = this.rowTpl;
46005         
46006         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46007         var labelWidth = 100;
46008         
46009         if ((this.labelAlign != 'top')) {
46010             if (typeof this.labelWidth == 'number') {
46011                 labelWidth = this.labelWidth
46012             }
46013             this.padWidth =  20 + labelWidth;
46014             
46015         }
46016         
46017         Roo.form.Column.superclass.onRender.call(this, ct, position);
46018         if(this.width){
46019             this.el.setWidth(this.width);
46020         }
46021         if(this.height){
46022             this.el.setHeight(this.height);
46023         }
46024     },
46025     
46026     // private
46027     renderField : function(f){
46028         f.fieldEl = this.fieldTpl.append(this.el, [
46029                f.id, f.fieldLabel,
46030                f.labelStyle||this.labelStyle||'',
46031                this.elementStyle||'',
46032                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46033                f.itemCls||this.itemCls||'',
46034                f.width ? f.width + this.padWidth : 160 + this.padWidth
46035        ],true);
46036     }
46037 });
46038  
46039
46040 /**
46041  * @class Roo.form.FieldSet
46042  * @extends Roo.form.Layout
46043  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46044  * @constructor
46045  * @param {Object} config Configuration options
46046  */
46047 Roo.form.FieldSet = function(config){
46048     Roo.form.FieldSet.superclass.constructor.call(this, config);
46049 };
46050
46051 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46052     /**
46053      * @cfg {String} legend
46054      * The text to display as the legend for the FieldSet (defaults to '')
46055      */
46056     /**
46057      * @cfg {String/Object} autoCreate
46058      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46059      */
46060
46061     // private
46062     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46063
46064     // private
46065     onRender : function(ct, position){
46066         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46067         if(this.legend){
46068             this.setLegend(this.legend);
46069         }
46070     },
46071
46072     // private
46073     setLegend : function(text){
46074         if(this.rendered){
46075             this.el.child('legend').update(text);
46076         }
46077     }
46078 });/*
46079  * Based on:
46080  * Ext JS Library 1.1.1
46081  * Copyright(c) 2006-2007, Ext JS, LLC.
46082  *
46083  * Originally Released Under LGPL - original licence link has changed is not relivant.
46084  *
46085  * Fork - LGPL
46086  * <script type="text/javascript">
46087  */
46088 /**
46089  * @class Roo.form.VTypes
46090  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46091  * @singleton
46092  */
46093 Roo.form.VTypes = function(){
46094     // closure these in so they are only created once.
46095     var alpha = /^[a-zA-Z_]+$/;
46096     var alphanum = /^[a-zA-Z0-9_]+$/;
46097     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46098     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46099
46100     // All these messages and functions are configurable
46101     return {
46102         /**
46103          * The function used to validate email addresses
46104          * @param {String} value The email address
46105          */
46106         'email' : function(v){
46107             return email.test(v);
46108         },
46109         /**
46110          * The error text to display when the email validation function returns false
46111          * @type String
46112          */
46113         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46114         /**
46115          * The keystroke filter mask to be applied on email input
46116          * @type RegExp
46117          */
46118         'emailMask' : /[a-z0-9_\.\-@]/i,
46119
46120         /**
46121          * The function used to validate URLs
46122          * @param {String} value The URL
46123          */
46124         'url' : function(v){
46125             return url.test(v);
46126         },
46127         /**
46128          * The error text to display when the url validation function returns false
46129          * @type String
46130          */
46131         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46132         
46133         /**
46134          * The function used to validate alpha values
46135          * @param {String} value The value
46136          */
46137         'alpha' : function(v){
46138             return alpha.test(v);
46139         },
46140         /**
46141          * The error text to display when the alpha validation function returns false
46142          * @type String
46143          */
46144         'alphaText' : 'This field should only contain letters and _',
46145         /**
46146          * The keystroke filter mask to be applied on alpha input
46147          * @type RegExp
46148          */
46149         'alphaMask' : /[a-z_]/i,
46150
46151         /**
46152          * The function used to validate alphanumeric values
46153          * @param {String} value The value
46154          */
46155         'alphanum' : function(v){
46156             return alphanum.test(v);
46157         },
46158         /**
46159          * The error text to display when the alphanumeric validation function returns false
46160          * @type String
46161          */
46162         'alphanumText' : 'This field should only contain letters, numbers and _',
46163         /**
46164          * The keystroke filter mask to be applied on alphanumeric input
46165          * @type RegExp
46166          */
46167         'alphanumMask' : /[a-z0-9_]/i
46168     };
46169 }();//<script type="text/javascript">
46170
46171 /**
46172  * @class Roo.form.FCKeditor
46173  * @extends Roo.form.TextArea
46174  * Wrapper around the FCKEditor http://www.fckeditor.net
46175  * @constructor
46176  * Creates a new FCKeditor
46177  * @param {Object} config Configuration options
46178  */
46179 Roo.form.FCKeditor = function(config){
46180     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46181     this.addEvents({
46182          /**
46183          * @event editorinit
46184          * Fired when the editor is initialized - you can add extra handlers here..
46185          * @param {FCKeditor} this
46186          * @param {Object} the FCK object.
46187          */
46188         editorinit : true
46189     });
46190     
46191     
46192 };
46193 Roo.form.FCKeditor.editors = { };
46194 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46195 {
46196     //defaultAutoCreate : {
46197     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46198     //},
46199     // private
46200     /**
46201      * @cfg {Object} fck options - see fck manual for details.
46202      */
46203     fckconfig : false,
46204     
46205     /**
46206      * @cfg {Object} fck toolbar set (Basic or Default)
46207      */
46208     toolbarSet : 'Basic',
46209     /**
46210      * @cfg {Object} fck BasePath
46211      */ 
46212     basePath : '/fckeditor/',
46213     
46214     
46215     frame : false,
46216     
46217     value : '',
46218     
46219    
46220     onRender : function(ct, position)
46221     {
46222         if(!this.el){
46223             this.defaultAutoCreate = {
46224                 tag: "textarea",
46225                 style:"width:300px;height:60px;",
46226                 autocomplete: "off"
46227             };
46228         }
46229         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46230         /*
46231         if(this.grow){
46232             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46233             if(this.preventScrollbars){
46234                 this.el.setStyle("overflow", "hidden");
46235             }
46236             this.el.setHeight(this.growMin);
46237         }
46238         */
46239         //console.log('onrender' + this.getId() );
46240         Roo.form.FCKeditor.editors[this.getId()] = this;
46241          
46242
46243         this.replaceTextarea() ;
46244         
46245     },
46246     
46247     getEditor : function() {
46248         return this.fckEditor;
46249     },
46250     /**
46251      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46252      * @param {Mixed} value The value to set
46253      */
46254     
46255     
46256     setValue : function(value)
46257     {
46258         //console.log('setValue: ' + value);
46259         
46260         if(typeof(value) == 'undefined') { // not sure why this is happending...
46261             return;
46262         }
46263         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46264         
46265         //if(!this.el || !this.getEditor()) {
46266         //    this.value = value;
46267             //this.setValue.defer(100,this,[value]);    
46268         //    return;
46269         //} 
46270         
46271         if(!this.getEditor()) {
46272             return;
46273         }
46274         
46275         this.getEditor().SetData(value);
46276         
46277         //
46278
46279     },
46280
46281     /**
46282      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46283      * @return {Mixed} value The field value
46284      */
46285     getValue : function()
46286     {
46287         
46288         if (this.frame && this.frame.dom.style.display == 'none') {
46289             return Roo.form.FCKeditor.superclass.getValue.call(this);
46290         }
46291         
46292         if(!this.el || !this.getEditor()) {
46293            
46294            // this.getValue.defer(100,this); 
46295             return this.value;
46296         }
46297        
46298         
46299         var value=this.getEditor().GetData();
46300         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46301         return Roo.form.FCKeditor.superclass.getValue.call(this);
46302         
46303
46304     },
46305
46306     /**
46307      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46308      * @return {Mixed} value The field value
46309      */
46310     getRawValue : function()
46311     {
46312         if (this.frame && this.frame.dom.style.display == 'none') {
46313             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46314         }
46315         
46316         if(!this.el || !this.getEditor()) {
46317             //this.getRawValue.defer(100,this); 
46318             return this.value;
46319             return;
46320         }
46321         
46322         
46323         
46324         var value=this.getEditor().GetData();
46325         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46326         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46327          
46328     },
46329     
46330     setSize : function(w,h) {
46331         
46332         
46333         
46334         //if (this.frame && this.frame.dom.style.display == 'none') {
46335         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46336         //    return;
46337         //}
46338         //if(!this.el || !this.getEditor()) {
46339         //    this.setSize.defer(100,this, [w,h]); 
46340         //    return;
46341         //}
46342         
46343         
46344         
46345         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46346         
46347         this.frame.dom.setAttribute('width', w);
46348         this.frame.dom.setAttribute('height', h);
46349         this.frame.setSize(w,h);
46350         
46351     },
46352     
46353     toggleSourceEdit : function(value) {
46354         
46355       
46356          
46357         this.el.dom.style.display = value ? '' : 'none';
46358         this.frame.dom.style.display = value ?  'none' : '';
46359         
46360     },
46361     
46362     
46363     focus: function(tag)
46364     {
46365         if (this.frame.dom.style.display == 'none') {
46366             return Roo.form.FCKeditor.superclass.focus.call(this);
46367         }
46368         if(!this.el || !this.getEditor()) {
46369             this.focus.defer(100,this, [tag]); 
46370             return;
46371         }
46372         
46373         
46374         
46375         
46376         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46377         this.getEditor().Focus();
46378         if (tgs.length) {
46379             if (!this.getEditor().Selection.GetSelection()) {
46380                 this.focus.defer(100,this, [tag]); 
46381                 return;
46382             }
46383             
46384             
46385             var r = this.getEditor().EditorDocument.createRange();
46386             r.setStart(tgs[0],0);
46387             r.setEnd(tgs[0],0);
46388             this.getEditor().Selection.GetSelection().removeAllRanges();
46389             this.getEditor().Selection.GetSelection().addRange(r);
46390             this.getEditor().Focus();
46391         }
46392         
46393     },
46394     
46395     
46396     
46397     replaceTextarea : function()
46398     {
46399         if ( document.getElementById( this.getId() + '___Frame' ) )
46400             return ;
46401         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46402         //{
46403             // We must check the elements firstly using the Id and then the name.
46404         var oTextarea = document.getElementById( this.getId() );
46405         
46406         var colElementsByName = document.getElementsByName( this.getId() ) ;
46407          
46408         oTextarea.style.display = 'none' ;
46409
46410         if ( oTextarea.tabIndex ) {            
46411             this.TabIndex = oTextarea.tabIndex ;
46412         }
46413         
46414         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46415         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46416         this.frame = Roo.get(this.getId() + '___Frame')
46417     },
46418     
46419     _getConfigHtml : function()
46420     {
46421         var sConfig = '' ;
46422
46423         for ( var o in this.fckconfig ) {
46424             sConfig += sConfig.length > 0  ? '&amp;' : '';
46425             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46426         }
46427
46428         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46429     },
46430     
46431     
46432     _getIFrameHtml : function()
46433     {
46434         var sFile = 'fckeditor.html' ;
46435         /* no idea what this is about..
46436         try
46437         {
46438             if ( (/fcksource=true/i).test( window.top.location.search ) )
46439                 sFile = 'fckeditor.original.html' ;
46440         }
46441         catch (e) { 
46442         */
46443
46444         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46445         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46446         
46447         
46448         var html = '<iframe id="' + this.getId() +
46449             '___Frame" src="' + sLink +
46450             '" width="' + this.width +
46451             '" height="' + this.height + '"' +
46452             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46453             ' frameborder="0" scrolling="no"></iframe>' ;
46454
46455         return html ;
46456     },
46457     
46458     _insertHtmlBefore : function( html, element )
46459     {
46460         if ( element.insertAdjacentHTML )       {
46461             // IE
46462             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46463         } else { // Gecko
46464             var oRange = document.createRange() ;
46465             oRange.setStartBefore( element ) ;
46466             var oFragment = oRange.createContextualFragment( html );
46467             element.parentNode.insertBefore( oFragment, element ) ;
46468         }
46469     }
46470     
46471     
46472   
46473     
46474     
46475     
46476     
46477
46478 });
46479
46480 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46481
46482 function FCKeditor_OnComplete(editorInstance){
46483     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46484     f.fckEditor = editorInstance;
46485     //console.log("loaded");
46486     f.fireEvent('editorinit', f, editorInstance);
46487
46488   
46489
46490  
46491
46492
46493
46494
46495
46496
46497
46498
46499
46500
46501
46502
46503
46504
46505
46506 //<script type="text/javascript">
46507 /**
46508  * @class Roo.form.GridField
46509  * @extends Roo.form.Field
46510  * Embed a grid (or editable grid into a form)
46511  * STATUS ALPHA
46512  * 
46513  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46514  * it needs 
46515  * xgrid.store = Roo.data.Store
46516  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46517  * xgrid.store.reader = Roo.data.JsonReader 
46518  * 
46519  * 
46520  * @constructor
46521  * Creates a new GridField
46522  * @param {Object} config Configuration options
46523  */
46524 Roo.form.GridField = function(config){
46525     Roo.form.GridField.superclass.constructor.call(this, config);
46526      
46527 };
46528
46529 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46530     /**
46531      * @cfg {Number} width  - used to restrict width of grid..
46532      */
46533     width : 100,
46534     /**
46535      * @cfg {Number} height - used to restrict height of grid..
46536      */
46537     height : 50,
46538      /**
46539      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46540          * 
46541          *}
46542      */
46543     xgrid : false, 
46544     /**
46545      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46546      * {tag: "input", type: "checkbox", autocomplete: "off"})
46547      */
46548    // defaultAutoCreate : { tag: 'div' },
46549     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46550     /**
46551      * @cfg {String} addTitle Text to include for adding a title.
46552      */
46553     addTitle : false,
46554     //
46555     onResize : function(){
46556         Roo.form.Field.superclass.onResize.apply(this, arguments);
46557     },
46558
46559     initEvents : function(){
46560         // Roo.form.Checkbox.superclass.initEvents.call(this);
46561         // has no events...
46562        
46563     },
46564
46565
46566     getResizeEl : function(){
46567         return this.wrap;
46568     },
46569
46570     getPositionEl : function(){
46571         return this.wrap;
46572     },
46573
46574     // private
46575     onRender : function(ct, position){
46576         
46577         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46578         var style = this.style;
46579         delete this.style;
46580         
46581         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46582         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46583         this.viewEl = this.wrap.createChild({ tag: 'div' });
46584         if (style) {
46585             this.viewEl.applyStyles(style);
46586         }
46587         if (this.width) {
46588             this.viewEl.setWidth(this.width);
46589         }
46590         if (this.height) {
46591             this.viewEl.setHeight(this.height);
46592         }
46593         //if(this.inputValue !== undefined){
46594         //this.setValue(this.value);
46595         
46596         
46597         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46598         
46599         
46600         this.grid.render();
46601         this.grid.getDataSource().on('remove', this.refreshValue, this);
46602         this.grid.getDataSource().on('update', this.refreshValue, this);
46603         this.grid.on('afteredit', this.refreshValue, this);
46604  
46605     },
46606      
46607     
46608     /**
46609      * Sets the value of the item. 
46610      * @param {String} either an object  or a string..
46611      */
46612     setValue : function(v){
46613         //this.value = v;
46614         v = v || []; // empty set..
46615         // this does not seem smart - it really only affects memoryproxy grids..
46616         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46617             var ds = this.grid.getDataSource();
46618             // assumes a json reader..
46619             var data = {}
46620             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46621             ds.loadData( data);
46622         }
46623         // clear selection so it does not get stale.
46624         if (this.grid.sm) { 
46625             this.grid.sm.clearSelections();
46626         }
46627         
46628         Roo.form.GridField.superclass.setValue.call(this, v);
46629         this.refreshValue();
46630         // should load data in the grid really....
46631     },
46632     
46633     // private
46634     refreshValue: function() {
46635          var val = [];
46636         this.grid.getDataSource().each(function(r) {
46637             val.push(r.data);
46638         });
46639         this.el.dom.value = Roo.encode(val);
46640     }
46641     
46642      
46643     
46644     
46645 });/*
46646  * Based on:
46647  * Ext JS Library 1.1.1
46648  * Copyright(c) 2006-2007, Ext JS, LLC.
46649  *
46650  * Originally Released Under LGPL - original licence link has changed is not relivant.
46651  *
46652  * Fork - LGPL
46653  * <script type="text/javascript">
46654  */
46655 /**
46656  * @class Roo.form.DisplayField
46657  * @extends Roo.form.Field
46658  * A generic Field to display non-editable data.
46659  * @constructor
46660  * Creates a new Display Field item.
46661  * @param {Object} config Configuration options
46662  */
46663 Roo.form.DisplayField = function(config){
46664     Roo.form.DisplayField.superclass.constructor.call(this, config);
46665     
46666 };
46667
46668 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46669     inputType:      'hidden',
46670     allowBlank:     true,
46671     readOnly:         true,
46672     
46673  
46674     /**
46675      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46676      */
46677     focusClass : undefined,
46678     /**
46679      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46680      */
46681     fieldClass: 'x-form-field',
46682     
46683      /**
46684      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46685      */
46686     valueRenderer: undefined,
46687     
46688     width: 100,
46689     /**
46690      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46691      * {tag: "input", type: "checkbox", autocomplete: "off"})
46692      */
46693      
46694  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46695
46696     onResize : function(){
46697         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46698         
46699     },
46700
46701     initEvents : function(){
46702         // Roo.form.Checkbox.superclass.initEvents.call(this);
46703         // has no events...
46704        
46705     },
46706
46707
46708     getResizeEl : function(){
46709         return this.wrap;
46710     },
46711
46712     getPositionEl : function(){
46713         return this.wrap;
46714     },
46715
46716     // private
46717     onRender : function(ct, position){
46718         
46719         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46720         //if(this.inputValue !== undefined){
46721         this.wrap = this.el.wrap();
46722         
46723         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46724         
46725         if (this.bodyStyle) {
46726             this.viewEl.applyStyles(this.bodyStyle);
46727         }
46728         //this.viewEl.setStyle('padding', '2px');
46729         
46730         this.setValue(this.value);
46731         
46732     },
46733 /*
46734     // private
46735     initValue : Roo.emptyFn,
46736
46737   */
46738
46739         // private
46740     onClick : function(){
46741         
46742     },
46743
46744     /**
46745      * Sets the checked state of the checkbox.
46746      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46747      */
46748     setValue : function(v){
46749         this.value = v;
46750         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46751         // this might be called before we have a dom element..
46752         if (!this.viewEl) {
46753             return;
46754         }
46755         this.viewEl.dom.innerHTML = html;
46756         Roo.form.DisplayField.superclass.setValue.call(this, v);
46757
46758     }
46759 });/*
46760  * 
46761  * Licence- LGPL
46762  * 
46763  */
46764
46765 /**
46766  * @class Roo.form.DayPicker
46767  * @extends Roo.form.Field
46768  * A Day picker show [M] [T] [W] ....
46769  * @constructor
46770  * Creates a new Day Picker
46771  * @param {Object} config Configuration options
46772  */
46773 Roo.form.DayPicker= function(config){
46774     Roo.form.DayPicker.superclass.constructor.call(this, config);
46775      
46776 };
46777
46778 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46779     /**
46780      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46781      */
46782     focusClass : undefined,
46783     /**
46784      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46785      */
46786     fieldClass: "x-form-field",
46787    
46788     /**
46789      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46790      * {tag: "input", type: "checkbox", autocomplete: "off"})
46791      */
46792     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46793     
46794    
46795     actionMode : 'viewEl', 
46796     //
46797     // private
46798  
46799     inputType : 'hidden',
46800     
46801      
46802     inputElement: false, // real input element?
46803     basedOn: false, // ????
46804     
46805     isFormField: true, // not sure where this is needed!!!!
46806
46807     onResize : function(){
46808         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46809         if(!this.boxLabel){
46810             this.el.alignTo(this.wrap, 'c-c');
46811         }
46812     },
46813
46814     initEvents : function(){
46815         Roo.form.Checkbox.superclass.initEvents.call(this);
46816         this.el.on("click", this.onClick,  this);
46817         this.el.on("change", this.onClick,  this);
46818     },
46819
46820
46821     getResizeEl : function(){
46822         return this.wrap;
46823     },
46824
46825     getPositionEl : function(){
46826         return this.wrap;
46827     },
46828
46829     
46830     // private
46831     onRender : function(ct, position){
46832         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46833        
46834         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46835         
46836         var r1 = '<table><tr>';
46837         var r2 = '<tr class="x-form-daypick-icons">';
46838         for (var i=0; i < 7; i++) {
46839             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46840             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46841         }
46842         
46843         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46844         viewEl.select('img').on('click', this.onClick, this);
46845         this.viewEl = viewEl;   
46846         
46847         
46848         // this will not work on Chrome!!!
46849         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46850         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46851         
46852         
46853           
46854
46855     },
46856
46857     // private
46858     initValue : Roo.emptyFn,
46859
46860     /**
46861      * Returns the checked state of the checkbox.
46862      * @return {Boolean} True if checked, else false
46863      */
46864     getValue : function(){
46865         return this.el.dom.value;
46866         
46867     },
46868
46869         // private
46870     onClick : function(e){ 
46871         //this.setChecked(!this.checked);
46872         Roo.get(e.target).toggleClass('x-menu-item-checked');
46873         this.refreshValue();
46874         //if(this.el.dom.checked != this.checked){
46875         //    this.setValue(this.el.dom.checked);
46876        // }
46877     },
46878     
46879     // private
46880     refreshValue : function()
46881     {
46882         var val = '';
46883         this.viewEl.select('img',true).each(function(e,i,n)  {
46884             val += e.is(".x-menu-item-checked") ? String(n) : '';
46885         });
46886         this.setValue(val, true);
46887     },
46888
46889     /**
46890      * Sets the checked state of the checkbox.
46891      * On is always based on a string comparison between inputValue and the param.
46892      * @param {Boolean/String} value - the value to set 
46893      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46894      */
46895     setValue : function(v,suppressEvent){
46896         if (!this.el.dom) {
46897             return;
46898         }
46899         var old = this.el.dom.value ;
46900         this.el.dom.value = v;
46901         if (suppressEvent) {
46902             return ;
46903         }
46904          
46905         // update display..
46906         this.viewEl.select('img',true).each(function(e,i,n)  {
46907             
46908             var on = e.is(".x-menu-item-checked");
46909             var newv = v.indexOf(String(n)) > -1;
46910             if (on != newv) {
46911                 e.toggleClass('x-menu-item-checked');
46912             }
46913             
46914         });
46915         
46916         
46917         this.fireEvent('change', this, v, old);
46918         
46919         
46920     },
46921    
46922     // handle setting of hidden value by some other method!!?!?
46923     setFromHidden: function()
46924     {
46925         if(!this.el){
46926             return;
46927         }
46928         //console.log("SET FROM HIDDEN");
46929         //alert('setFrom hidden');
46930         this.setValue(this.el.dom.value);
46931     },
46932     
46933     onDestroy : function()
46934     {
46935         if(this.viewEl){
46936             Roo.get(this.viewEl).remove();
46937         }
46938          
46939         Roo.form.DayPicker.superclass.onDestroy.call(this);
46940     }
46941
46942 });/*
46943  * RooJS Library 1.1.1
46944  * Copyright(c) 2008-2011  Alan Knowles
46945  *
46946  * License - LGPL
46947  */
46948  
46949
46950 /**
46951  * @class Roo.form.ComboCheck
46952  * @extends Roo.form.ComboBox
46953  * A combobox for multiple select items.
46954  *
46955  * FIXME - could do with a reset button..
46956  * 
46957  * @constructor
46958  * Create a new ComboCheck
46959  * @param {Object} config Configuration options
46960  */
46961 Roo.form.ComboCheck = function(config){
46962     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46963     // should verify some data...
46964     // like
46965     // hiddenName = required..
46966     // displayField = required
46967     // valudField == required
46968     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46969     var _t = this;
46970     Roo.each(req, function(e) {
46971         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46972             throw "Roo.form.ComboCheck : missing value for: " + e;
46973         }
46974     });
46975     
46976     
46977 };
46978
46979 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46980      
46981      
46982     editable : false,
46983      
46984     selectedClass: 'x-menu-item-checked', 
46985     
46986     // private
46987     onRender : function(ct, position){
46988         var _t = this;
46989         
46990         
46991         
46992         if(!this.tpl){
46993             var cls = 'x-combo-list';
46994
46995             
46996             this.tpl =  new Roo.Template({
46997                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46998                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46999                    '<span>{' + this.displayField + '}</span>' +
47000                     '</div>' 
47001                 
47002             });
47003         }
47004  
47005         
47006         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47007         this.view.singleSelect = false;
47008         this.view.multiSelect = true;
47009         this.view.toggleSelect = true;
47010         this.pageTb.add(new Roo.Toolbar.Fill(), {
47011             
47012             text: 'Done',
47013             handler: function()
47014             {
47015                 _t.collapse();
47016             }
47017         });
47018     },
47019     
47020     onViewOver : function(e, t){
47021         // do nothing...
47022         return;
47023         
47024     },
47025     
47026     onViewClick : function(doFocus,index){
47027         return;
47028         
47029     },
47030     select: function () {
47031         //Roo.log("SELECT CALLED");
47032     },
47033      
47034     selectByValue : function(xv, scrollIntoView){
47035         var ar = this.getValueArray();
47036         var sels = [];
47037         
47038         Roo.each(ar, function(v) {
47039             if(v === undefined || v === null){
47040                 return;
47041             }
47042             var r = this.findRecord(this.valueField, v);
47043             if(r){
47044                 sels.push(this.store.indexOf(r))
47045                 
47046             }
47047         },this);
47048         this.view.select(sels);
47049         return false;
47050     },
47051     
47052     
47053     
47054     onSelect : function(record, index){
47055        // Roo.log("onselect Called");
47056        // this is only called by the clear button now..
47057         this.view.clearSelections();
47058         this.setValue('[]');
47059         if (this.value != this.valueBefore) {
47060             this.fireEvent('change', this, this.value, this.valueBefore);
47061             this.valueBefore = this.value;
47062         }
47063     },
47064     getValueArray : function()
47065     {
47066         var ar = [] ;
47067         
47068         try {
47069             //Roo.log(this.value);
47070             if (typeof(this.value) == 'undefined') {
47071                 return [];
47072             }
47073             var ar = Roo.decode(this.value);
47074             return  ar instanceof Array ? ar : []; //?? valid?
47075             
47076         } catch(e) {
47077             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47078             return [];
47079         }
47080          
47081     },
47082     expand : function ()
47083     {
47084         
47085         Roo.form.ComboCheck.superclass.expand.call(this);
47086         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47087         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47088         
47089
47090     },
47091     
47092     collapse : function(){
47093         Roo.form.ComboCheck.superclass.collapse.call(this);
47094         var sl = this.view.getSelectedIndexes();
47095         var st = this.store;
47096         var nv = [];
47097         var tv = [];
47098         var r;
47099         Roo.each(sl, function(i) {
47100             r = st.getAt(i);
47101             nv.push(r.get(this.valueField));
47102         },this);
47103         this.setValue(Roo.encode(nv));
47104         if (this.value != this.valueBefore) {
47105
47106             this.fireEvent('change', this, this.value, this.valueBefore);
47107             this.valueBefore = this.value;
47108         }
47109         
47110     },
47111     
47112     setValue : function(v){
47113         // Roo.log(v);
47114         this.value = v;
47115         
47116         var vals = this.getValueArray();
47117         var tv = [];
47118         Roo.each(vals, function(k) {
47119             var r = this.findRecord(this.valueField, k);
47120             if(r){
47121                 tv.push(r.data[this.displayField]);
47122             }else if(this.valueNotFoundText !== undefined){
47123                 tv.push( this.valueNotFoundText );
47124             }
47125         },this);
47126        // Roo.log(tv);
47127         
47128         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47129         this.hiddenField.value = v;
47130         this.value = v;
47131     }
47132     
47133 });/*
47134  * Based on:
47135  * Ext JS Library 1.1.1
47136  * Copyright(c) 2006-2007, Ext JS, LLC.
47137  *
47138  * Originally Released Under LGPL - original licence link has changed is not relivant.
47139  *
47140  * Fork - LGPL
47141  * <script type="text/javascript">
47142  */
47143  
47144 /**
47145  * @class Roo.form.Signature
47146  * @extends Roo.form.Field
47147  * Signature field.  
47148  * @constructor
47149  * 
47150  * @param {Object} config Configuration options
47151  */
47152
47153 Roo.form.Signature = function(config){
47154     Roo.form.Signature.superclass.constructor.call(this, config);
47155     
47156     this.addEvents({// not in used??
47157          /**
47158          * @event confirm
47159          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47160              * @param {Roo.form.Signature} combo This combo box
47161              */
47162         'confirm' : true,
47163         /**
47164          * @event reset
47165          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47166              * @param {Roo.form.ComboBox} combo This combo box
47167              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47168              */
47169         'reset' : true
47170     });
47171 };
47172
47173 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47174     /**
47175      * @cfg {Object} labels Label to use when rendering a form.
47176      * defaults to 
47177      * labels : { 
47178      *      clear : "Clear",
47179      *      confirm : "Confirm"
47180      *  }
47181      */
47182     labels : { 
47183         clear : "Clear",
47184         confirm : "Confirm"
47185     },
47186     /**
47187      * @cfg {Number} width The signature panel width (defaults to 300)
47188      */
47189     width: 300,
47190     /**
47191      * @cfg {Number} height The signature panel height (defaults to 100)
47192      */
47193     height : 100,
47194     /**
47195      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47196      */
47197     allowBlank : false,
47198     
47199     //private
47200     // {Object} signPanel The signature SVG panel element (defaults to {})
47201     signPanel : {},
47202     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47203     isMouseDown : false,
47204     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47205     isConfirmed : false,
47206     // {String} signatureTmp SVG mapping string (defaults to empty string)
47207     signatureTmp : '',
47208     
47209     
47210     defaultAutoCreate : { // modified by initCompnoent..
47211         tag: "input",
47212         type:"hidden"
47213     },
47214
47215     // private
47216     onRender : function(ct, position){
47217         
47218         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47219         
47220         this.wrap = this.el.wrap({
47221             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47222         });
47223         
47224         this.createToolbar(this);
47225         this.signPanel = this.wrap.createChild({
47226                 tag: 'div',
47227                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47228             }, this.el
47229         );
47230             
47231         this.svgID = Roo.id();
47232         this.svgEl = this.signPanel.createChild({
47233               xmlns : 'http://www.w3.org/2000/svg',
47234               tag : 'svg',
47235               id : this.svgID + "-svg",
47236               width: this.width,
47237               height: this.height,
47238               viewBox: '0 0 '+this.width+' '+this.height,
47239               cn : [
47240                 {
47241                     tag: "rect",
47242                     id: this.svgID + "-svg-r",
47243                     width: this.width,
47244                     height: this.height,
47245                     fill: "#ffa"
47246                 },
47247                 {
47248                     tag: "line",
47249                     id: this.svgID + "-svg-l",
47250                     x1: "0", // start
47251                     y1: (this.height*0.8), // start set the line in 80% of height
47252                     x2: this.width, // end
47253                     y2: (this.height*0.8), // end set the line in 80% of height
47254                     'stroke': "#666",
47255                     'stroke-width': "1",
47256                     'stroke-dasharray': "3",
47257                     'shape-rendering': "crispEdges",
47258                     'pointer-events': "none"
47259                 },
47260                 {
47261                     tag: "path",
47262                     id: this.svgID + "-svg-p",
47263                     'stroke': "navy",
47264                     'stroke-width': "3",
47265                     'fill': "none",
47266                     'pointer-events': 'none'
47267                 }
47268               ]
47269         });
47270         this.createSVG();
47271         this.svgBox = this.svgEl.dom.getScreenCTM();
47272     },
47273     createSVG : function(){ 
47274         var svg = this.signPanel;
47275         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47276         var t = this;
47277
47278         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47279         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47280         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47281         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47282         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47283         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47284         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47285         
47286     },
47287     isTouchEvent : function(e){
47288         return e.type.match(/^touch/);
47289     },
47290     getCoords : function (e) {
47291         var pt    = this.svgEl.dom.createSVGPoint();
47292         pt.x = e.clientX; 
47293         pt.y = e.clientY;
47294         if (this.isTouchEvent(e)) {
47295             pt.x =  e.targetTouches[0].clientX 
47296             pt.y = e.targetTouches[0].clientY;
47297         }
47298         var a = this.svgEl.dom.getScreenCTM();
47299         var b = a.inverse();
47300         var mx = pt.matrixTransform(b);
47301         return mx.x + ',' + mx.y;
47302     },
47303     //mouse event headler 
47304     down : function (e) {
47305         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47306         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47307         
47308         this.isMouseDown = true;
47309         
47310         e.preventDefault();
47311     },
47312     move : function (e) {
47313         if (this.isMouseDown) {
47314             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47315             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47316         }
47317         
47318         e.preventDefault();
47319     },
47320     up : function (e) {
47321         this.isMouseDown = false;
47322         var sp = this.signatureTmp.split(' ');
47323         
47324         if(sp.length > 1){
47325             if(!sp[sp.length-2].match(/^L/)){
47326                 sp.pop();
47327                 sp.pop();
47328                 sp.push("");
47329                 this.signatureTmp = sp.join(" ");
47330             }
47331         }
47332         if(this.getValue() != this.signatureTmp){
47333             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47334             this.isConfirmed = false;
47335         }
47336         e.preventDefault();
47337     },
47338     
47339     /**
47340      * Protected method that will not generally be called directly. It
47341      * is called when the editor creates its toolbar. Override this method if you need to
47342      * add custom toolbar buttons.
47343      * @param {HtmlEditor} editor
47344      */
47345     createToolbar : function(editor){
47346          function btn(id, toggle, handler){
47347             var xid = fid + '-'+ id ;
47348             return {
47349                 id : xid,
47350                 cmd : id,
47351                 cls : 'x-btn-icon x-edit-'+id,
47352                 enableToggle:toggle !== false,
47353                 scope: editor, // was editor...
47354                 handler:handler||editor.relayBtnCmd,
47355                 clickEvent:'mousedown',
47356                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47357                 tabIndex:-1
47358             };
47359         }
47360         
47361         
47362         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47363         this.tb = tb;
47364         this.tb.add(
47365            {
47366                 cls : ' x-signature-btn x-signature-'+id,
47367                 scope: editor, // was editor...
47368                 handler: this.reset,
47369                 clickEvent:'mousedown',
47370                 text: this.labels.clear
47371             },
47372             {
47373                  xtype : 'Fill',
47374                  xns: Roo.Toolbar
47375             }, 
47376             {
47377                 cls : '  x-signature-btn x-signature-'+id,
47378                 scope: editor, // was editor...
47379                 handler: this.confirmHandler,
47380                 clickEvent:'mousedown',
47381                 text: this.labels.confirm
47382             }
47383         );
47384     
47385     },
47386     //public
47387     /**
47388      * when user is clicked confirm then show this image.....
47389      * 
47390      * @return {String} Image Data URI
47391      */
47392     getImageDataURI : function(){
47393         var svg = this.svgEl.dom.parentNode.innerHTML;
47394         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47395         return src; 
47396     },
47397     /**
47398      * 
47399      * @return {Boolean} this.isConfirmed
47400      */
47401     getConfirmed : function(){
47402         return this.isConfirmed;
47403     },
47404     /**
47405      * 
47406      * @return {Number} this.width
47407      */
47408     getWidth : function(){
47409         return this.width;
47410     },
47411     /**
47412      * 
47413      * @return {Number} this.height
47414      */
47415     getHeight : function(){
47416         return this.height;
47417     },
47418     // private
47419     getSignature : function(){
47420         return this.signatureTmp;
47421     },
47422     // private
47423     reset : function(){
47424         this.signatureTmp = '';
47425         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47426         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47427         this.isConfirmed = false;
47428         Roo.form.Signature.superclass.reset.call(this);
47429     },
47430     setSignature : function(s){
47431         this.signatureTmp = s;
47432         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47433         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47434         this.setValue(s);
47435         this.isConfirmed = false;
47436         Roo.form.Signature.superclass.reset.call(this);
47437     }, 
47438     test : function(){
47439 //        Roo.log(this.signPanel.dom.contentWindow.up())
47440     },
47441     //private
47442     setConfirmed : function(){
47443         
47444         
47445         
47446 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47447     },
47448     // private
47449     confirmHandler : function(){
47450         if(!this.getSignature()){
47451             return;
47452         }
47453         
47454         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47455         this.setValue(this.getSignature());
47456         this.isConfirmed = true;
47457         
47458         this.fireEvent('confirm', this);
47459     },
47460     // private
47461     // Subclasses should provide the validation implementation by overriding this
47462     validateValue : function(value){
47463         if(this.allowBlank){
47464             return true;
47465         }
47466         
47467         if(this.isConfirmed){
47468             return true;
47469         }
47470         return false;
47471     }
47472 });/*
47473  * Based on:
47474  * Ext JS Library 1.1.1
47475  * Copyright(c) 2006-2007, Ext JS, LLC.
47476  *
47477  * Originally Released Under LGPL - original licence link has changed is not relivant.
47478  *
47479  * Fork - LGPL
47480  * <script type="text/javascript">
47481  */
47482  
47483
47484 /**
47485  * @class Roo.form.ComboBox
47486  * @extends Roo.form.TriggerField
47487  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47488  * @constructor
47489  * Create a new ComboBox.
47490  * @param {Object} config Configuration options
47491  */
47492 Roo.form.Select = function(config){
47493     Roo.form.Select.superclass.constructor.call(this, config);
47494      
47495 };
47496
47497 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47498     /**
47499      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47500      */
47501     /**
47502      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47503      * rendering into an Roo.Editor, defaults to false)
47504      */
47505     /**
47506      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47507      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47508      */
47509     /**
47510      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47511      */
47512     /**
47513      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47514      * the dropdown list (defaults to undefined, with no header element)
47515      */
47516
47517      /**
47518      * @cfg {String/Roo.Template} tpl The template to use to render the output
47519      */
47520      
47521     // private
47522     defaultAutoCreate : {tag: "select"  },
47523     /**
47524      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47525      */
47526     listWidth: undefined,
47527     /**
47528      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47529      * mode = 'remote' or 'text' if mode = 'local')
47530      */
47531     displayField: undefined,
47532     /**
47533      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47534      * mode = 'remote' or 'value' if mode = 'local'). 
47535      * Note: use of a valueField requires the user make a selection
47536      * in order for a value to be mapped.
47537      */
47538     valueField: undefined,
47539     
47540     
47541     /**
47542      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47543      * field's data value (defaults to the underlying DOM element's name)
47544      */
47545     hiddenName: undefined,
47546     /**
47547      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47548      */
47549     listClass: '',
47550     /**
47551      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47552      */
47553     selectedClass: 'x-combo-selected',
47554     /**
47555      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47556      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47557      * which displays a downward arrow icon).
47558      */
47559     triggerClass : 'x-form-arrow-trigger',
47560     /**
47561      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47562      */
47563     shadow:'sides',
47564     /**
47565      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47566      * anchor positions (defaults to 'tl-bl')
47567      */
47568     listAlign: 'tl-bl?',
47569     /**
47570      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47571      */
47572     maxHeight: 300,
47573     /**
47574      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47575      * query specified by the allQuery config option (defaults to 'query')
47576      */
47577     triggerAction: 'query',
47578     /**
47579      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47580      * (defaults to 4, does not apply if editable = false)
47581      */
47582     minChars : 4,
47583     /**
47584      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47585      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47586      */
47587     typeAhead: false,
47588     /**
47589      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47590      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47591      */
47592     queryDelay: 500,
47593     /**
47594      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47595      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47596      */
47597     pageSize: 0,
47598     /**
47599      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47600      * when editable = true (defaults to false)
47601      */
47602     selectOnFocus:false,
47603     /**
47604      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47605      */
47606     queryParam: 'query',
47607     /**
47608      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47609      * when mode = 'remote' (defaults to 'Loading...')
47610      */
47611     loadingText: 'Loading...',
47612     /**
47613      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47614      */
47615     resizable: false,
47616     /**
47617      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47618      */
47619     handleHeight : 8,
47620     /**
47621      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47622      * traditional select (defaults to true)
47623      */
47624     editable: true,
47625     /**
47626      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47627      */
47628     allQuery: '',
47629     /**
47630      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47631      */
47632     mode: 'remote',
47633     /**
47634      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47635      * listWidth has a higher value)
47636      */
47637     minListWidth : 70,
47638     /**
47639      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47640      * allow the user to set arbitrary text into the field (defaults to false)
47641      */
47642     forceSelection:false,
47643     /**
47644      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47645      * if typeAhead = true (defaults to 250)
47646      */
47647     typeAheadDelay : 250,
47648     /**
47649      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47650      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47651      */
47652     valueNotFoundText : undefined,
47653     
47654     /**
47655      * @cfg {String} defaultValue The value displayed after loading the store.
47656      */
47657     defaultValue: '',
47658     
47659     /**
47660      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47661      */
47662     blockFocus : false,
47663     
47664     /**
47665      * @cfg {Boolean} disableClear Disable showing of clear button.
47666      */
47667     disableClear : false,
47668     /**
47669      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47670      */
47671     alwaysQuery : false,
47672     
47673     //private
47674     addicon : false,
47675     editicon: false,
47676     
47677     // element that contains real text value.. (when hidden is used..)
47678      
47679     // private
47680     onRender : function(ct, position){
47681         Roo.form.Field.prototype.onRender.call(this, ct, position);
47682         
47683         if(this.store){
47684             this.store.on('beforeload', this.onBeforeLoad, this);
47685             this.store.on('load', this.onLoad, this);
47686             this.store.on('loadexception', this.onLoadException, this);
47687             this.store.load({});
47688         }
47689         
47690         
47691         
47692     },
47693
47694     // private
47695     initEvents : function(){
47696         //Roo.form.ComboBox.superclass.initEvents.call(this);
47697  
47698     },
47699
47700     onDestroy : function(){
47701        
47702         if(this.store){
47703             this.store.un('beforeload', this.onBeforeLoad, this);
47704             this.store.un('load', this.onLoad, this);
47705             this.store.un('loadexception', this.onLoadException, this);
47706         }
47707         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47708     },
47709
47710     // private
47711     fireKey : function(e){
47712         if(e.isNavKeyPress() && !this.list.isVisible()){
47713             this.fireEvent("specialkey", this, e);
47714         }
47715     },
47716
47717     // private
47718     onResize: function(w, h){
47719         
47720         return; 
47721     
47722         
47723     },
47724
47725     /**
47726      * Allow or prevent the user from directly editing the field text.  If false is passed,
47727      * the user will only be able to select from the items defined in the dropdown list.  This method
47728      * is the runtime equivalent of setting the 'editable' config option at config time.
47729      * @param {Boolean} value True to allow the user to directly edit the field text
47730      */
47731     setEditable : function(value){
47732          
47733     },
47734
47735     // private
47736     onBeforeLoad : function(){
47737         
47738         Roo.log("Select before load");
47739         return;
47740     
47741         this.innerList.update(this.loadingText ?
47742                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47743         //this.restrictHeight();
47744         this.selectedIndex = -1;
47745     },
47746
47747     // private
47748     onLoad : function(){
47749
47750     
47751         var dom = this.el.dom;
47752         dom.innerHTML = '';
47753          var od = dom.ownerDocument;
47754          
47755         if (this.emptyText) {
47756             var op = od.createElement('option');
47757             op.setAttribute('value', '');
47758             op.innerHTML = String.format('{0}', this.emptyText);
47759             dom.appendChild(op);
47760         }
47761         if(this.store.getCount() > 0){
47762            
47763             var vf = this.valueField;
47764             var df = this.displayField;
47765             this.store.data.each(function(r) {
47766                 // which colmsn to use... testing - cdoe / title..
47767                 var op = od.createElement('option');
47768                 op.setAttribute('value', r.data[vf]);
47769                 op.innerHTML = String.format('{0}', r.data[df]);
47770                 dom.appendChild(op);
47771             });
47772             if (typeof(this.defaultValue != 'undefined')) {
47773                 this.setValue(this.defaultValue);
47774             }
47775             
47776              
47777         }else{
47778             //this.onEmptyResults();
47779         }
47780         //this.el.focus();
47781     },
47782     // private
47783     onLoadException : function()
47784     {
47785         dom.innerHTML = '';
47786             
47787         Roo.log("Select on load exception");
47788         return;
47789     
47790         this.collapse();
47791         Roo.log(this.store.reader.jsonData);
47792         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47793             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47794         }
47795         
47796         
47797     },
47798     // private
47799     onTypeAhead : function(){
47800          
47801     },
47802
47803     // private
47804     onSelect : function(record, index){
47805         Roo.log('on select?');
47806         return;
47807         if(this.fireEvent('beforeselect', this, record, index) !== false){
47808             this.setFromData(index > -1 ? record.data : false);
47809             this.collapse();
47810             this.fireEvent('select', this, record, index);
47811         }
47812     },
47813
47814     /**
47815      * Returns the currently selected field value or empty string if no value is set.
47816      * @return {String} value The selected value
47817      */
47818     getValue : function(){
47819         var dom = this.el.dom;
47820         this.value = dom.options[dom.selectedIndex].value;
47821         return this.value;
47822         
47823     },
47824
47825     /**
47826      * Clears any text/value currently set in the field
47827      */
47828     clearValue : function(){
47829         this.value = '';
47830         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47831         
47832     },
47833
47834     /**
47835      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47836      * will be displayed in the field.  If the value does not match the data value of an existing item,
47837      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47838      * Otherwise the field will be blank (although the value will still be set).
47839      * @param {String} value The value to match
47840      */
47841     setValue : function(v){
47842         var d = this.el.dom;
47843         for (var i =0; i < d.options.length;i++) {
47844             if (v == d.options[i].value) {
47845                 d.selectedIndex = i;
47846                 this.value = v;
47847                 return;
47848             }
47849         }
47850         this.clearValue();
47851     },
47852     /**
47853      * @property {Object} the last set data for the element
47854      */
47855     
47856     lastData : false,
47857     /**
47858      * Sets the value of the field based on a object which is related to the record format for the store.
47859      * @param {Object} value the value to set as. or false on reset?
47860      */
47861     setFromData : function(o){
47862         Roo.log('setfrom data?');
47863          
47864         
47865         
47866     },
47867     // private
47868     reset : function(){
47869         this.clearValue();
47870     },
47871     // private
47872     findRecord : function(prop, value){
47873         
47874         return false;
47875     
47876         var record;
47877         if(this.store.getCount() > 0){
47878             this.store.each(function(r){
47879                 if(r.data[prop] == value){
47880                     record = r;
47881                     return false;
47882                 }
47883                 return true;
47884             });
47885         }
47886         return record;
47887     },
47888     
47889     getName: function()
47890     {
47891         // returns hidden if it's set..
47892         if (!this.rendered) {return ''};
47893         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47894         
47895     },
47896      
47897
47898     
47899
47900     // private
47901     onEmptyResults : function(){
47902         Roo.log('empty results');
47903         //this.collapse();
47904     },
47905
47906     /**
47907      * Returns true if the dropdown list is expanded, else false.
47908      */
47909     isExpanded : function(){
47910         return false;
47911     },
47912
47913     /**
47914      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47915      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47916      * @param {String} value The data value of the item to select
47917      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47918      * selected item if it is not currently in view (defaults to true)
47919      * @return {Boolean} True if the value matched an item in the list, else false
47920      */
47921     selectByValue : function(v, scrollIntoView){
47922         Roo.log('select By Value');
47923         return false;
47924     
47925         if(v !== undefined && v !== null){
47926             var r = this.findRecord(this.valueField || this.displayField, v);
47927             if(r){
47928                 this.select(this.store.indexOf(r), scrollIntoView);
47929                 return true;
47930             }
47931         }
47932         return false;
47933     },
47934
47935     /**
47936      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47937      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47938      * @param {Number} index The zero-based index of the list item to select
47939      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47940      * selected item if it is not currently in view (defaults to true)
47941      */
47942     select : function(index, scrollIntoView){
47943         Roo.log('select ');
47944         return  ;
47945         
47946         this.selectedIndex = index;
47947         this.view.select(index);
47948         if(scrollIntoView !== false){
47949             var el = this.view.getNode(index);
47950             if(el){
47951                 this.innerList.scrollChildIntoView(el, false);
47952             }
47953         }
47954     },
47955
47956       
47957
47958     // private
47959     validateBlur : function(){
47960         
47961         return;
47962         
47963     },
47964
47965     // private
47966     initQuery : function(){
47967         this.doQuery(this.getRawValue());
47968     },
47969
47970     // private
47971     doForce : function(){
47972         if(this.el.dom.value.length > 0){
47973             this.el.dom.value =
47974                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47975              
47976         }
47977     },
47978
47979     /**
47980      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47981      * query allowing the query action to be canceled if needed.
47982      * @param {String} query The SQL query to execute
47983      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47984      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47985      * saved in the current store (defaults to false)
47986      */
47987     doQuery : function(q, forceAll){
47988         
47989         Roo.log('doQuery?');
47990         if(q === undefined || q === null){
47991             q = '';
47992         }
47993         var qe = {
47994             query: q,
47995             forceAll: forceAll,
47996             combo: this,
47997             cancel:false
47998         };
47999         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48000             return false;
48001         }
48002         q = qe.query;
48003         forceAll = qe.forceAll;
48004         if(forceAll === true || (q.length >= this.minChars)){
48005             if(this.lastQuery != q || this.alwaysQuery){
48006                 this.lastQuery = q;
48007                 if(this.mode == 'local'){
48008                     this.selectedIndex = -1;
48009                     if(forceAll){
48010                         this.store.clearFilter();
48011                     }else{
48012                         this.store.filter(this.displayField, q);
48013                     }
48014                     this.onLoad();
48015                 }else{
48016                     this.store.baseParams[this.queryParam] = q;
48017                     this.store.load({
48018                         params: this.getParams(q)
48019                     });
48020                     this.expand();
48021                 }
48022             }else{
48023                 this.selectedIndex = -1;
48024                 this.onLoad();   
48025             }
48026         }
48027     },
48028
48029     // private
48030     getParams : function(q){
48031         var p = {};
48032         //p[this.queryParam] = q;
48033         if(this.pageSize){
48034             p.start = 0;
48035             p.limit = this.pageSize;
48036         }
48037         return p;
48038     },
48039
48040     /**
48041      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48042      */
48043     collapse : function(){
48044         
48045     },
48046
48047     // private
48048     collapseIf : function(e){
48049         
48050     },
48051
48052     /**
48053      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48054      */
48055     expand : function(){
48056         
48057     } ,
48058
48059     // private
48060      
48061
48062     /** 
48063     * @cfg {Boolean} grow 
48064     * @hide 
48065     */
48066     /** 
48067     * @cfg {Number} growMin 
48068     * @hide 
48069     */
48070     /** 
48071     * @cfg {Number} growMax 
48072     * @hide 
48073     */
48074     /**
48075      * @hide
48076      * @method autoSize
48077      */
48078     
48079     setWidth : function()
48080     {
48081         
48082     },
48083     getResizeEl : function(){
48084         return this.el;
48085     }
48086 });//<script type="text/javasscript">
48087  
48088
48089 /**
48090  * @class Roo.DDView
48091  * A DnD enabled version of Roo.View.
48092  * @param {Element/String} container The Element in which to create the View.
48093  * @param {String} tpl The template string used to create the markup for each element of the View
48094  * @param {Object} config The configuration properties. These include all the config options of
48095  * {@link Roo.View} plus some specific to this class.<br>
48096  * <p>
48097  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48098  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48099  * <p>
48100  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48101 .x-view-drag-insert-above {
48102         border-top:1px dotted #3366cc;
48103 }
48104 .x-view-drag-insert-below {
48105         border-bottom:1px dotted #3366cc;
48106 }
48107 </code></pre>
48108  * 
48109  */
48110  
48111 Roo.DDView = function(container, tpl, config) {
48112     Roo.DDView.superclass.constructor.apply(this, arguments);
48113     this.getEl().setStyle("outline", "0px none");
48114     this.getEl().unselectable();
48115     if (this.dragGroup) {
48116                 this.setDraggable(this.dragGroup.split(","));
48117     }
48118     if (this.dropGroup) {
48119                 this.setDroppable(this.dropGroup.split(","));
48120     }
48121     if (this.deletable) {
48122         this.setDeletable();
48123     }
48124     this.isDirtyFlag = false;
48125         this.addEvents({
48126                 "drop" : true
48127         });
48128 };
48129
48130 Roo.extend(Roo.DDView, Roo.View, {
48131 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48132 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48133 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48134 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48135
48136         isFormField: true,
48137
48138         reset: Roo.emptyFn,
48139         
48140         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48141
48142         validate: function() {
48143                 return true;
48144         },
48145         
48146         destroy: function() {
48147                 this.purgeListeners();
48148                 this.getEl.removeAllListeners();
48149                 this.getEl().remove();
48150                 if (this.dragZone) {
48151                         if (this.dragZone.destroy) {
48152                                 this.dragZone.destroy();
48153                         }
48154                 }
48155                 if (this.dropZone) {
48156                         if (this.dropZone.destroy) {
48157                                 this.dropZone.destroy();
48158                         }
48159                 }
48160         },
48161
48162 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48163         getName: function() {
48164                 return this.name;
48165         },
48166
48167 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48168         setValue: function(v) {
48169                 if (!this.store) {
48170                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48171                 }
48172                 var data = {};
48173                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48174                 this.store.proxy = new Roo.data.MemoryProxy(data);
48175                 this.store.load();
48176         },
48177
48178 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48179         getValue: function() {
48180                 var result = '(';
48181                 this.store.each(function(rec) {
48182                         result += rec.id + ',';
48183                 });
48184                 return result.substr(0, result.length - 1) + ')';
48185         },
48186         
48187         getIds: function() {
48188                 var i = 0, result = new Array(this.store.getCount());
48189                 this.store.each(function(rec) {
48190                         result[i++] = rec.id;
48191                 });
48192                 return result;
48193         },
48194         
48195         isDirty: function() {
48196                 return this.isDirtyFlag;
48197         },
48198
48199 /**
48200  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48201  *      whole Element becomes the target, and this causes the drop gesture to append.
48202  */
48203     getTargetFromEvent : function(e) {
48204                 var target = e.getTarget();
48205                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48206                 target = target.parentNode;
48207                 }
48208                 if (!target) {
48209                         target = this.el.dom.lastChild || this.el.dom;
48210                 }
48211                 return target;
48212     },
48213
48214 /**
48215  *      Create the drag data which consists of an object which has the property "ddel" as
48216  *      the drag proxy element. 
48217  */
48218     getDragData : function(e) {
48219         var target = this.findItemFromChild(e.getTarget());
48220                 if(target) {
48221                         this.handleSelection(e);
48222                         var selNodes = this.getSelectedNodes();
48223             var dragData = {
48224                 source: this,
48225                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48226                 nodes: selNodes,
48227                 records: []
48228                         };
48229                         var selectedIndices = this.getSelectedIndexes();
48230                         for (var i = 0; i < selectedIndices.length; i++) {
48231                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48232                         }
48233                         if (selNodes.length == 1) {
48234                                 dragData.ddel = target.cloneNode(true); // the div element
48235                         } else {
48236                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48237                                 div.className = 'multi-proxy';
48238                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48239                                         div.appendChild(selNodes[i].cloneNode(true));
48240                                 }
48241                                 dragData.ddel = div;
48242                         }
48243             //console.log(dragData)
48244             //console.log(dragData.ddel.innerHTML)
48245                         return dragData;
48246                 }
48247         //console.log('nodragData')
48248                 return false;
48249     },
48250     
48251 /**     Specify to which ddGroup items in this DDView may be dragged. */
48252     setDraggable: function(ddGroup) {
48253         if (ddGroup instanceof Array) {
48254                 Roo.each(ddGroup, this.setDraggable, this);
48255                 return;
48256         }
48257         if (this.dragZone) {
48258                 this.dragZone.addToGroup(ddGroup);
48259         } else {
48260                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48261                                 containerScroll: true,
48262                                 ddGroup: ddGroup 
48263
48264                         });
48265 //                      Draggability implies selection. DragZone's mousedown selects the element.
48266                         if (!this.multiSelect) { this.singleSelect = true; }
48267
48268 //                      Wire the DragZone's handlers up to methods in *this*
48269                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48270                 }
48271     },
48272
48273 /**     Specify from which ddGroup this DDView accepts drops. */
48274     setDroppable: function(ddGroup) {
48275         if (ddGroup instanceof Array) {
48276                 Roo.each(ddGroup, this.setDroppable, this);
48277                 return;
48278         }
48279         if (this.dropZone) {
48280                 this.dropZone.addToGroup(ddGroup);
48281         } else {
48282                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48283                                 containerScroll: true,
48284                                 ddGroup: ddGroup
48285                         });
48286
48287 //                      Wire the DropZone's handlers up to methods in *this*
48288                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48289                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48290                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48291                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48292                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48293                 }
48294     },
48295
48296 /**     Decide whether to drop above or below a View node. */
48297     getDropPoint : function(e, n, dd){
48298         if (n == this.el.dom) { return "above"; }
48299                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48300                 var c = t + (b - t) / 2;
48301                 var y = Roo.lib.Event.getPageY(e);
48302                 if(y <= c) {
48303                         return "above";
48304                 }else{
48305                         return "below";
48306                 }
48307     },
48308
48309     onNodeEnter : function(n, dd, e, data){
48310                 return false;
48311     },
48312     
48313     onNodeOver : function(n, dd, e, data){
48314                 var pt = this.getDropPoint(e, n, dd);
48315                 // set the insert point style on the target node
48316                 var dragElClass = this.dropNotAllowed;
48317                 if (pt) {
48318                         var targetElClass;
48319                         if (pt == "above"){
48320                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48321                                 targetElClass = "x-view-drag-insert-above";
48322                         } else {
48323                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48324                                 targetElClass = "x-view-drag-insert-below";
48325                         }
48326                         if (this.lastInsertClass != targetElClass){
48327                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48328                                 this.lastInsertClass = targetElClass;
48329                         }
48330                 }
48331                 return dragElClass;
48332         },
48333
48334     onNodeOut : function(n, dd, e, data){
48335                 this.removeDropIndicators(n);
48336     },
48337
48338     onNodeDrop : function(n, dd, e, data){
48339         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48340                 return false;
48341         }
48342         var pt = this.getDropPoint(e, n, dd);
48343                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48344                 if (pt == "below") { insertAt++; }
48345                 for (var i = 0; i < data.records.length; i++) {
48346                         var r = data.records[i];
48347                         var dup = this.store.getById(r.id);
48348                         if (dup && (dd != this.dragZone)) {
48349                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48350                         } else {
48351                                 if (data.copy) {
48352                                         this.store.insert(insertAt++, r.copy());
48353                                 } else {
48354                                         data.source.isDirtyFlag = true;
48355                                         r.store.remove(r);
48356                                         this.store.insert(insertAt++, r);
48357                                 }
48358                                 this.isDirtyFlag = true;
48359                         }
48360                 }
48361                 this.dragZone.cachedTarget = null;
48362                 return true;
48363     },
48364
48365     removeDropIndicators : function(n){
48366                 if(n){
48367                         Roo.fly(n).removeClass([
48368                                 "x-view-drag-insert-above",
48369                                 "x-view-drag-insert-below"]);
48370                         this.lastInsertClass = "_noclass";
48371                 }
48372     },
48373
48374 /**
48375  *      Utility method. Add a delete option to the DDView's context menu.
48376  *      @param {String} imageUrl The URL of the "delete" icon image.
48377  */
48378         setDeletable: function(imageUrl) {
48379                 if (!this.singleSelect && !this.multiSelect) {
48380                         this.singleSelect = true;
48381                 }
48382                 var c = this.getContextMenu();
48383                 this.contextMenu.on("itemclick", function(item) {
48384                         switch (item.id) {
48385                                 case "delete":
48386                                         this.remove(this.getSelectedIndexes());
48387                                         break;
48388                         }
48389                 }, this);
48390                 this.contextMenu.add({
48391                         icon: imageUrl,
48392                         id: "delete",
48393                         text: 'Delete'
48394                 });
48395         },
48396         
48397 /**     Return the context menu for this DDView. */
48398         getContextMenu: function() {
48399                 if (!this.contextMenu) {
48400 //                      Create the View's context menu
48401                         this.contextMenu = new Roo.menu.Menu({
48402                                 id: this.id + "-contextmenu"
48403                         });
48404                         this.el.on("contextmenu", this.showContextMenu, this);
48405                 }
48406                 return this.contextMenu;
48407         },
48408         
48409         disableContextMenu: function() {
48410                 if (this.contextMenu) {
48411                         this.el.un("contextmenu", this.showContextMenu, this);
48412                 }
48413         },
48414
48415         showContextMenu: function(e, item) {
48416         item = this.findItemFromChild(e.getTarget());
48417                 if (item) {
48418                         e.stopEvent();
48419                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48420                         this.contextMenu.showAt(e.getXY());
48421             }
48422     },
48423
48424 /**
48425  *      Remove {@link Roo.data.Record}s at the specified indices.
48426  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48427  */
48428     remove: function(selectedIndices) {
48429                 selectedIndices = [].concat(selectedIndices);
48430                 for (var i = 0; i < selectedIndices.length; i++) {
48431                         var rec = this.store.getAt(selectedIndices[i]);
48432                         this.store.remove(rec);
48433                 }
48434     },
48435
48436 /**
48437  *      Double click fires the event, but also, if this is draggable, and there is only one other
48438  *      related DropZone, it transfers the selected node.
48439  */
48440     onDblClick : function(e){
48441         var item = this.findItemFromChild(e.getTarget());
48442         if(item){
48443             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48444                 return false;
48445             }
48446             if (this.dragGroup) {
48447                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48448                     while (targets.indexOf(this.dropZone) > -1) {
48449                             targets.remove(this.dropZone);
48450                                 }
48451                     if (targets.length == 1) {
48452                                         this.dragZone.cachedTarget = null;
48453                         var el = Roo.get(targets[0].getEl());
48454                         var box = el.getBox(true);
48455                         targets[0].onNodeDrop(el.dom, {
48456                                 target: el.dom,
48457                                 xy: [box.x, box.y + box.height - 1]
48458                         }, null, this.getDragData(e));
48459                     }
48460                 }
48461         }
48462     },
48463     
48464     handleSelection: function(e) {
48465                 this.dragZone.cachedTarget = null;
48466         var item = this.findItemFromChild(e.getTarget());
48467         if (!item) {
48468                 this.clearSelections(true);
48469                 return;
48470         }
48471                 if (item && (this.multiSelect || this.singleSelect)){
48472                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48473                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48474                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48475                                 this.unselect(item);
48476                         } else {
48477                                 this.select(item, this.multiSelect && e.ctrlKey);
48478                                 this.lastSelection = item;
48479                         }
48480                 }
48481     },
48482
48483     onItemClick : function(item, index, e){
48484                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48485                         return false;
48486                 }
48487                 return true;
48488     },
48489
48490     unselect : function(nodeInfo, suppressEvent){
48491                 var node = this.getNode(nodeInfo);
48492                 if(node && this.isSelected(node)){
48493                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48494                                 Roo.fly(node).removeClass(this.selectedClass);
48495                                 this.selections.remove(node);
48496                                 if(!suppressEvent){
48497                                         this.fireEvent("selectionchange", this, this.selections);
48498                                 }
48499                         }
48500                 }
48501     }
48502 });
48503 /*
48504  * Based on:
48505  * Ext JS Library 1.1.1
48506  * Copyright(c) 2006-2007, Ext JS, LLC.
48507  *
48508  * Originally Released Under LGPL - original licence link has changed is not relivant.
48509  *
48510  * Fork - LGPL
48511  * <script type="text/javascript">
48512  */
48513  
48514 /**
48515  * @class Roo.LayoutManager
48516  * @extends Roo.util.Observable
48517  * Base class for layout managers.
48518  */
48519 Roo.LayoutManager = function(container, config){
48520     Roo.LayoutManager.superclass.constructor.call(this);
48521     this.el = Roo.get(container);
48522     // ie scrollbar fix
48523     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48524         document.body.scroll = "no";
48525     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48526         this.el.position('relative');
48527     }
48528     this.id = this.el.id;
48529     this.el.addClass("x-layout-container");
48530     /** false to disable window resize monitoring @type Boolean */
48531     this.monitorWindowResize = true;
48532     this.regions = {};
48533     this.addEvents({
48534         /**
48535          * @event layout
48536          * Fires when a layout is performed. 
48537          * @param {Roo.LayoutManager} this
48538          */
48539         "layout" : true,
48540         /**
48541          * @event regionresized
48542          * Fires when the user resizes a region. 
48543          * @param {Roo.LayoutRegion} region The resized region
48544          * @param {Number} newSize The new size (width for east/west, height for north/south)
48545          */
48546         "regionresized" : true,
48547         /**
48548          * @event regioncollapsed
48549          * Fires when a region is collapsed. 
48550          * @param {Roo.LayoutRegion} region The collapsed region
48551          */
48552         "regioncollapsed" : true,
48553         /**
48554          * @event regionexpanded
48555          * Fires when a region is expanded.  
48556          * @param {Roo.LayoutRegion} region The expanded region
48557          */
48558         "regionexpanded" : true
48559     });
48560     this.updating = false;
48561     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48562 };
48563
48564 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48565     /**
48566      * Returns true if this layout is currently being updated
48567      * @return {Boolean}
48568      */
48569     isUpdating : function(){
48570         return this.updating; 
48571     },
48572     
48573     /**
48574      * Suspend the LayoutManager from doing auto-layouts while
48575      * making multiple add or remove calls
48576      */
48577     beginUpdate : function(){
48578         this.updating = true;    
48579     },
48580     
48581     /**
48582      * Restore auto-layouts and optionally disable the manager from performing a layout
48583      * @param {Boolean} noLayout true to disable a layout update 
48584      */
48585     endUpdate : function(noLayout){
48586         this.updating = false;
48587         if(!noLayout){
48588             this.layout();
48589         }    
48590     },
48591     
48592     layout: function(){
48593         
48594     },
48595     
48596     onRegionResized : function(region, newSize){
48597         this.fireEvent("regionresized", region, newSize);
48598         this.layout();
48599     },
48600     
48601     onRegionCollapsed : function(region){
48602         this.fireEvent("regioncollapsed", region);
48603     },
48604     
48605     onRegionExpanded : function(region){
48606         this.fireEvent("regionexpanded", region);
48607     },
48608         
48609     /**
48610      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48611      * performs box-model adjustments.
48612      * @return {Object} The size as an object {width: (the width), height: (the height)}
48613      */
48614     getViewSize : function(){
48615         var size;
48616         if(this.el.dom != document.body){
48617             size = this.el.getSize();
48618         }else{
48619             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48620         }
48621         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48622         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48623         return size;
48624     },
48625     
48626     /**
48627      * Returns the Element this layout is bound to.
48628      * @return {Roo.Element}
48629      */
48630     getEl : function(){
48631         return this.el;
48632     },
48633     
48634     /**
48635      * Returns the specified region.
48636      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48637      * @return {Roo.LayoutRegion}
48638      */
48639     getRegion : function(target){
48640         return this.regions[target.toLowerCase()];
48641     },
48642     
48643     onWindowResize : function(){
48644         if(this.monitorWindowResize){
48645             this.layout();
48646         }
48647     }
48648 });/*
48649  * Based on:
48650  * Ext JS Library 1.1.1
48651  * Copyright(c) 2006-2007, Ext JS, LLC.
48652  *
48653  * Originally Released Under LGPL - original licence link has changed is not relivant.
48654  *
48655  * Fork - LGPL
48656  * <script type="text/javascript">
48657  */
48658 /**
48659  * @class Roo.BorderLayout
48660  * @extends Roo.LayoutManager
48661  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48662  * please see: <br><br>
48663  * <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>
48664  * <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>
48665  * Example:
48666  <pre><code>
48667  var layout = new Roo.BorderLayout(document.body, {
48668     north: {
48669         initialSize: 25,
48670         titlebar: false
48671     },
48672     west: {
48673         split:true,
48674         initialSize: 200,
48675         minSize: 175,
48676         maxSize: 400,
48677         titlebar: true,
48678         collapsible: true
48679     },
48680     east: {
48681         split:true,
48682         initialSize: 202,
48683         minSize: 175,
48684         maxSize: 400,
48685         titlebar: true,
48686         collapsible: true
48687     },
48688     south: {
48689         split:true,
48690         initialSize: 100,
48691         minSize: 100,
48692         maxSize: 200,
48693         titlebar: true,
48694         collapsible: true
48695     },
48696     center: {
48697         titlebar: true,
48698         autoScroll:true,
48699         resizeTabs: true,
48700         minTabWidth: 50,
48701         preferredTabWidth: 150
48702     }
48703 });
48704
48705 // shorthand
48706 var CP = Roo.ContentPanel;
48707
48708 layout.beginUpdate();
48709 layout.add("north", new CP("north", "North"));
48710 layout.add("south", new CP("south", {title: "South", closable: true}));
48711 layout.add("west", new CP("west", {title: "West"}));
48712 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48713 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48714 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48715 layout.getRegion("center").showPanel("center1");
48716 layout.endUpdate();
48717 </code></pre>
48718
48719 <b>The container the layout is rendered into can be either the body element or any other element.
48720 If it is not the body element, the container needs to either be an absolute positioned element,
48721 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48722 the container size if it is not the body element.</b>
48723
48724 * @constructor
48725 * Create a new BorderLayout
48726 * @param {String/HTMLElement/Element} container The container this layout is bound to
48727 * @param {Object} config Configuration options
48728  */
48729 Roo.BorderLayout = function(container, config){
48730     config = config || {};
48731     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48732     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48733     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48734         var target = this.factory.validRegions[i];
48735         if(config[target]){
48736             this.addRegion(target, config[target]);
48737         }
48738     }
48739 };
48740
48741 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48742     /**
48743      * Creates and adds a new region if it doesn't already exist.
48744      * @param {String} target The target region key (north, south, east, west or center).
48745      * @param {Object} config The regions config object
48746      * @return {BorderLayoutRegion} The new region
48747      */
48748     addRegion : function(target, config){
48749         if(!this.regions[target]){
48750             var r = this.factory.create(target, this, config);
48751             this.bindRegion(target, r);
48752         }
48753         return this.regions[target];
48754     },
48755
48756     // private (kinda)
48757     bindRegion : function(name, r){
48758         this.regions[name] = r;
48759         r.on("visibilitychange", this.layout, this);
48760         r.on("paneladded", this.layout, this);
48761         r.on("panelremoved", this.layout, this);
48762         r.on("invalidated", this.layout, this);
48763         r.on("resized", this.onRegionResized, this);
48764         r.on("collapsed", this.onRegionCollapsed, this);
48765         r.on("expanded", this.onRegionExpanded, this);
48766     },
48767
48768     /**
48769      * Performs a layout update.
48770      */
48771     layout : function(){
48772         if(this.updating) return;
48773         var size = this.getViewSize();
48774         var w = size.width;
48775         var h = size.height;
48776         var centerW = w;
48777         var centerH = h;
48778         var centerY = 0;
48779         var centerX = 0;
48780         //var x = 0, y = 0;
48781
48782         var rs = this.regions;
48783         var north = rs["north"];
48784         var south = rs["south"]; 
48785         var west = rs["west"];
48786         var east = rs["east"];
48787         var center = rs["center"];
48788         //if(this.hideOnLayout){ // not supported anymore
48789             //c.el.setStyle("display", "none");
48790         //}
48791         if(north && north.isVisible()){
48792             var b = north.getBox();
48793             var m = north.getMargins();
48794             b.width = w - (m.left+m.right);
48795             b.x = m.left;
48796             b.y = m.top;
48797             centerY = b.height + b.y + m.bottom;
48798             centerH -= centerY;
48799             north.updateBox(this.safeBox(b));
48800         }
48801         if(south && south.isVisible()){
48802             var b = south.getBox();
48803             var m = south.getMargins();
48804             b.width = w - (m.left+m.right);
48805             b.x = m.left;
48806             var totalHeight = (b.height + m.top + m.bottom);
48807             b.y = h - totalHeight + m.top;
48808             centerH -= totalHeight;
48809             south.updateBox(this.safeBox(b));
48810         }
48811         if(west && west.isVisible()){
48812             var b = west.getBox();
48813             var m = west.getMargins();
48814             b.height = centerH - (m.top+m.bottom);
48815             b.x = m.left;
48816             b.y = centerY + m.top;
48817             var totalWidth = (b.width + m.left + m.right);
48818             centerX += totalWidth;
48819             centerW -= totalWidth;
48820             west.updateBox(this.safeBox(b));
48821         }
48822         if(east && east.isVisible()){
48823             var b = east.getBox();
48824             var m = east.getMargins();
48825             b.height = centerH - (m.top+m.bottom);
48826             var totalWidth = (b.width + m.left + m.right);
48827             b.x = w - totalWidth + m.left;
48828             b.y = centerY + m.top;
48829             centerW -= totalWidth;
48830             east.updateBox(this.safeBox(b));
48831         }
48832         if(center){
48833             var m = center.getMargins();
48834             var centerBox = {
48835                 x: centerX + m.left,
48836                 y: centerY + m.top,
48837                 width: centerW - (m.left+m.right),
48838                 height: centerH - (m.top+m.bottom)
48839             };
48840             //if(this.hideOnLayout){
48841                 //center.el.setStyle("display", "block");
48842             //}
48843             center.updateBox(this.safeBox(centerBox));
48844         }
48845         this.el.repaint();
48846         this.fireEvent("layout", this);
48847     },
48848
48849     // private
48850     safeBox : function(box){
48851         box.width = Math.max(0, box.width);
48852         box.height = Math.max(0, box.height);
48853         return box;
48854     },
48855
48856     /**
48857      * Adds a ContentPanel (or subclass) to this layout.
48858      * @param {String} target The target region key (north, south, east, west or center).
48859      * @param {Roo.ContentPanel} panel The panel to add
48860      * @return {Roo.ContentPanel} The added panel
48861      */
48862     add : function(target, panel){
48863          
48864         target = target.toLowerCase();
48865         return this.regions[target].add(panel);
48866     },
48867
48868     /**
48869      * Remove a ContentPanel (or subclass) to this layout.
48870      * @param {String} target The target region key (north, south, east, west or center).
48871      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48872      * @return {Roo.ContentPanel} The removed panel
48873      */
48874     remove : function(target, panel){
48875         target = target.toLowerCase();
48876         return this.regions[target].remove(panel);
48877     },
48878
48879     /**
48880      * Searches all regions for a panel with the specified id
48881      * @param {String} panelId
48882      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48883      */
48884     findPanel : function(panelId){
48885         var rs = this.regions;
48886         for(var target in rs){
48887             if(typeof rs[target] != "function"){
48888                 var p = rs[target].getPanel(panelId);
48889                 if(p){
48890                     return p;
48891                 }
48892             }
48893         }
48894         return null;
48895     },
48896
48897     /**
48898      * Searches all regions for a panel with the specified id and activates (shows) it.
48899      * @param {String/ContentPanel} panelId The panels id or the panel itself
48900      * @return {Roo.ContentPanel} The shown panel or null
48901      */
48902     showPanel : function(panelId) {
48903       var rs = this.regions;
48904       for(var target in rs){
48905          var r = rs[target];
48906          if(typeof r != "function"){
48907             if(r.hasPanel(panelId)){
48908                return r.showPanel(panelId);
48909             }
48910          }
48911       }
48912       return null;
48913    },
48914
48915    /**
48916      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48917      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48918      */
48919     restoreState : function(provider){
48920         if(!provider){
48921             provider = Roo.state.Manager;
48922         }
48923         var sm = new Roo.LayoutStateManager();
48924         sm.init(this, provider);
48925     },
48926
48927     /**
48928      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48929      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48930      * a valid ContentPanel config object.  Example:
48931      * <pre><code>
48932 // Create the main layout
48933 var layout = new Roo.BorderLayout('main-ct', {
48934     west: {
48935         split:true,
48936         minSize: 175,
48937         titlebar: true
48938     },
48939     center: {
48940         title:'Components'
48941     }
48942 }, 'main-ct');
48943
48944 // Create and add multiple ContentPanels at once via configs
48945 layout.batchAdd({
48946    west: {
48947        id: 'source-files',
48948        autoCreate:true,
48949        title:'Ext Source Files',
48950        autoScroll:true,
48951        fitToFrame:true
48952    },
48953    center : {
48954        el: cview,
48955        autoScroll:true,
48956        fitToFrame:true,
48957        toolbar: tb,
48958        resizeEl:'cbody'
48959    }
48960 });
48961 </code></pre>
48962      * @param {Object} regions An object containing ContentPanel configs by region name
48963      */
48964     batchAdd : function(regions){
48965         this.beginUpdate();
48966         for(var rname in regions){
48967             var lr = this.regions[rname];
48968             if(lr){
48969                 this.addTypedPanels(lr, regions[rname]);
48970             }
48971         }
48972         this.endUpdate();
48973     },
48974
48975     // private
48976     addTypedPanels : function(lr, ps){
48977         if(typeof ps == 'string'){
48978             lr.add(new Roo.ContentPanel(ps));
48979         }
48980         else if(ps instanceof Array){
48981             for(var i =0, len = ps.length; i < len; i++){
48982                 this.addTypedPanels(lr, ps[i]);
48983             }
48984         }
48985         else if(!ps.events){ // raw config?
48986             var el = ps.el;
48987             delete ps.el; // prevent conflict
48988             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48989         }
48990         else {  // panel object assumed!
48991             lr.add(ps);
48992         }
48993     },
48994     /**
48995      * Adds a xtype elements to the layout.
48996      * <pre><code>
48997
48998 layout.addxtype({
48999        xtype : 'ContentPanel',
49000        region: 'west',
49001        items: [ .... ]
49002    }
49003 );
49004
49005 layout.addxtype({
49006         xtype : 'NestedLayoutPanel',
49007         region: 'west',
49008         layout: {
49009            center: { },
49010            west: { }   
49011         },
49012         items : [ ... list of content panels or nested layout panels.. ]
49013    }
49014 );
49015 </code></pre>
49016      * @param {Object} cfg Xtype definition of item to add.
49017      */
49018     addxtype : function(cfg)
49019     {
49020         // basically accepts a pannel...
49021         // can accept a layout region..!?!?
49022         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49023         
49024         if (!cfg.xtype.match(/Panel$/)) {
49025             return false;
49026         }
49027         var ret = false;
49028         
49029         if (typeof(cfg.region) == 'undefined') {
49030             Roo.log("Failed to add Panel, region was not set");
49031             Roo.log(cfg);
49032             return false;
49033         }
49034         var region = cfg.region;
49035         delete cfg.region;
49036         
49037           
49038         var xitems = [];
49039         if (cfg.items) {
49040             xitems = cfg.items;
49041             delete cfg.items;
49042         }
49043         var nb = false;
49044         
49045         switch(cfg.xtype) 
49046         {
49047             case 'ContentPanel':  // ContentPanel (el, cfg)
49048             case 'ScrollPanel':  // ContentPanel (el, cfg)
49049             case 'ViewPanel': 
49050                 if(cfg.autoCreate) {
49051                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49052                 } else {
49053                     var el = this.el.createChild();
49054                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49055                 }
49056                 
49057                 this.add(region, ret);
49058                 break;
49059             
49060             
49061             case 'TreePanel': // our new panel!
49062                 cfg.el = this.el.createChild();
49063                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49064                 this.add(region, ret);
49065                 break;
49066             
49067             case 'NestedLayoutPanel': 
49068                 // create a new Layout (which is  a Border Layout...
49069                 var el = this.el.createChild();
49070                 var clayout = cfg.layout;
49071                 delete cfg.layout;
49072                 clayout.items   = clayout.items  || [];
49073                 // replace this exitems with the clayout ones..
49074                 xitems = clayout.items;
49075                  
49076                 
49077                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49078                     cfg.background = false;
49079                 }
49080                 var layout = new Roo.BorderLayout(el, clayout);
49081                 
49082                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49083                 //console.log('adding nested layout panel '  + cfg.toSource());
49084                 this.add(region, ret);
49085                 nb = {}; /// find first...
49086                 break;
49087                 
49088             case 'GridPanel': 
49089             
49090                 // needs grid and region
49091                 
49092                 //var el = this.getRegion(region).el.createChild();
49093                 var el = this.el.createChild();
49094                 // create the grid first...
49095                 
49096                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49097                 delete cfg.grid;
49098                 if (region == 'center' && this.active ) {
49099                     cfg.background = false;
49100                 }
49101                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49102                 
49103                 this.add(region, ret);
49104                 if (cfg.background) {
49105                     ret.on('activate', function(gp) {
49106                         if (!gp.grid.rendered) {
49107                             gp.grid.render();
49108                         }
49109                     });
49110                 } else {
49111                     grid.render();
49112                 }
49113                 break;
49114            
49115            
49116            
49117                 
49118                 
49119                 
49120             default:
49121                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49122                     
49123                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49124                     this.add(region, ret);
49125                 } else {
49126                 
49127                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49128                     return null;
49129                 }
49130                 
49131              // GridPanel (grid, cfg)
49132             
49133         }
49134         this.beginUpdate();
49135         // add children..
49136         var region = '';
49137         var abn = {};
49138         Roo.each(xitems, function(i)  {
49139             region = nb && i.region ? i.region : false;
49140             
49141             var add = ret.addxtype(i);
49142            
49143             if (region) {
49144                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49145                 if (!i.background) {
49146                     abn[region] = nb[region] ;
49147                 }
49148             }
49149             
49150         });
49151         this.endUpdate();
49152
49153         // make the last non-background panel active..
49154         //if (nb) { Roo.log(abn); }
49155         if (nb) {
49156             
49157             for(var r in abn) {
49158                 region = this.getRegion(r);
49159                 if (region) {
49160                     // tried using nb[r], but it does not work..
49161                      
49162                     region.showPanel(abn[r]);
49163                    
49164                 }
49165             }
49166         }
49167         return ret;
49168         
49169     }
49170 });
49171
49172 /**
49173  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49174  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49175  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49176  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49177  * <pre><code>
49178 // shorthand
49179 var CP = Roo.ContentPanel;
49180
49181 var layout = Roo.BorderLayout.create({
49182     north: {
49183         initialSize: 25,
49184         titlebar: false,
49185         panels: [new CP("north", "North")]
49186     },
49187     west: {
49188         split:true,
49189         initialSize: 200,
49190         minSize: 175,
49191         maxSize: 400,
49192         titlebar: true,
49193         collapsible: true,
49194         panels: [new CP("west", {title: "West"})]
49195     },
49196     east: {
49197         split:true,
49198         initialSize: 202,
49199         minSize: 175,
49200         maxSize: 400,
49201         titlebar: true,
49202         collapsible: true,
49203         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49204     },
49205     south: {
49206         split:true,
49207         initialSize: 100,
49208         minSize: 100,
49209         maxSize: 200,
49210         titlebar: true,
49211         collapsible: true,
49212         panels: [new CP("south", {title: "South", closable: true})]
49213     },
49214     center: {
49215         titlebar: true,
49216         autoScroll:true,
49217         resizeTabs: true,
49218         minTabWidth: 50,
49219         preferredTabWidth: 150,
49220         panels: [
49221             new CP("center1", {title: "Close Me", closable: true}),
49222             new CP("center2", {title: "Center Panel", closable: false})
49223         ]
49224     }
49225 }, document.body);
49226
49227 layout.getRegion("center").showPanel("center1");
49228 </code></pre>
49229  * @param config
49230  * @param targetEl
49231  */
49232 Roo.BorderLayout.create = function(config, targetEl){
49233     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49234     layout.beginUpdate();
49235     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49236     for(var j = 0, jlen = regions.length; j < jlen; j++){
49237         var lr = regions[j];
49238         if(layout.regions[lr] && config[lr].panels){
49239             var r = layout.regions[lr];
49240             var ps = config[lr].panels;
49241             layout.addTypedPanels(r, ps);
49242         }
49243     }
49244     layout.endUpdate();
49245     return layout;
49246 };
49247
49248 // private
49249 Roo.BorderLayout.RegionFactory = {
49250     // private
49251     validRegions : ["north","south","east","west","center"],
49252
49253     // private
49254     create : function(target, mgr, config){
49255         target = target.toLowerCase();
49256         if(config.lightweight || config.basic){
49257             return new Roo.BasicLayoutRegion(mgr, config, target);
49258         }
49259         switch(target){
49260             case "north":
49261                 return new Roo.NorthLayoutRegion(mgr, config);
49262             case "south":
49263                 return new Roo.SouthLayoutRegion(mgr, config);
49264             case "east":
49265                 return new Roo.EastLayoutRegion(mgr, config);
49266             case "west":
49267                 return new Roo.WestLayoutRegion(mgr, config);
49268             case "center":
49269                 return new Roo.CenterLayoutRegion(mgr, config);
49270         }
49271         throw 'Layout region "'+target+'" not supported.';
49272     }
49273 };/*
49274  * Based on:
49275  * Ext JS Library 1.1.1
49276  * Copyright(c) 2006-2007, Ext JS, LLC.
49277  *
49278  * Originally Released Under LGPL - original licence link has changed is not relivant.
49279  *
49280  * Fork - LGPL
49281  * <script type="text/javascript">
49282  */
49283  
49284 /**
49285  * @class Roo.BasicLayoutRegion
49286  * @extends Roo.util.Observable
49287  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49288  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49289  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49290  */
49291 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49292     this.mgr = mgr;
49293     this.position  = pos;
49294     this.events = {
49295         /**
49296          * @scope Roo.BasicLayoutRegion
49297          */
49298         
49299         /**
49300          * @event beforeremove
49301          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49302          * @param {Roo.LayoutRegion} this
49303          * @param {Roo.ContentPanel} panel The panel
49304          * @param {Object} e The cancel event object
49305          */
49306         "beforeremove" : true,
49307         /**
49308          * @event invalidated
49309          * Fires when the layout for this region is changed.
49310          * @param {Roo.LayoutRegion} this
49311          */
49312         "invalidated" : true,
49313         /**
49314          * @event visibilitychange
49315          * Fires when this region is shown or hidden 
49316          * @param {Roo.LayoutRegion} this
49317          * @param {Boolean} visibility true or false
49318          */
49319         "visibilitychange" : true,
49320         /**
49321          * @event paneladded
49322          * Fires when a panel is added. 
49323          * @param {Roo.LayoutRegion} this
49324          * @param {Roo.ContentPanel} panel The panel
49325          */
49326         "paneladded" : true,
49327         /**
49328          * @event panelremoved
49329          * Fires when a panel is removed. 
49330          * @param {Roo.LayoutRegion} this
49331          * @param {Roo.ContentPanel} panel The panel
49332          */
49333         "panelremoved" : true,
49334         /**
49335          * @event collapsed
49336          * Fires when this region is collapsed.
49337          * @param {Roo.LayoutRegion} this
49338          */
49339         "collapsed" : true,
49340         /**
49341          * @event expanded
49342          * Fires when this region is expanded.
49343          * @param {Roo.LayoutRegion} this
49344          */
49345         "expanded" : true,
49346         /**
49347          * @event slideshow
49348          * Fires when this region is slid into view.
49349          * @param {Roo.LayoutRegion} this
49350          */
49351         "slideshow" : true,
49352         /**
49353          * @event slidehide
49354          * Fires when this region slides out of view. 
49355          * @param {Roo.LayoutRegion} this
49356          */
49357         "slidehide" : true,
49358         /**
49359          * @event panelactivated
49360          * Fires when a panel is activated. 
49361          * @param {Roo.LayoutRegion} this
49362          * @param {Roo.ContentPanel} panel The activated panel
49363          */
49364         "panelactivated" : true,
49365         /**
49366          * @event resized
49367          * Fires when the user resizes this region. 
49368          * @param {Roo.LayoutRegion} this
49369          * @param {Number} newSize The new size (width for east/west, height for north/south)
49370          */
49371         "resized" : true
49372     };
49373     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49374     this.panels = new Roo.util.MixedCollection();
49375     this.panels.getKey = this.getPanelId.createDelegate(this);
49376     this.box = null;
49377     this.activePanel = null;
49378     // ensure listeners are added...
49379     
49380     if (config.listeners || config.events) {
49381         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49382             listeners : config.listeners || {},
49383             events : config.events || {}
49384         });
49385     }
49386     
49387     if(skipConfig !== true){
49388         this.applyConfig(config);
49389     }
49390 };
49391
49392 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49393     getPanelId : function(p){
49394         return p.getId();
49395     },
49396     
49397     applyConfig : function(config){
49398         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49399         this.config = config;
49400         
49401     },
49402     
49403     /**
49404      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49405      * the width, for horizontal (north, south) the height.
49406      * @param {Number} newSize The new width or height
49407      */
49408     resizeTo : function(newSize){
49409         var el = this.el ? this.el :
49410                  (this.activePanel ? this.activePanel.getEl() : null);
49411         if(el){
49412             switch(this.position){
49413                 case "east":
49414                 case "west":
49415                     el.setWidth(newSize);
49416                     this.fireEvent("resized", this, newSize);
49417                 break;
49418                 case "north":
49419                 case "south":
49420                     el.setHeight(newSize);
49421                     this.fireEvent("resized", this, newSize);
49422                 break;                
49423             }
49424         }
49425     },
49426     
49427     getBox : function(){
49428         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49429     },
49430     
49431     getMargins : function(){
49432         return this.margins;
49433     },
49434     
49435     updateBox : function(box){
49436         this.box = box;
49437         var el = this.activePanel.getEl();
49438         el.dom.style.left = box.x + "px";
49439         el.dom.style.top = box.y + "px";
49440         this.activePanel.setSize(box.width, box.height);
49441     },
49442     
49443     /**
49444      * Returns the container element for this region.
49445      * @return {Roo.Element}
49446      */
49447     getEl : function(){
49448         return this.activePanel;
49449     },
49450     
49451     /**
49452      * Returns true if this region is currently visible.
49453      * @return {Boolean}
49454      */
49455     isVisible : function(){
49456         return this.activePanel ? true : false;
49457     },
49458     
49459     setActivePanel : function(panel){
49460         panel = this.getPanel(panel);
49461         if(this.activePanel && this.activePanel != panel){
49462             this.activePanel.setActiveState(false);
49463             this.activePanel.getEl().setLeftTop(-10000,-10000);
49464         }
49465         this.activePanel = panel;
49466         panel.setActiveState(true);
49467         if(this.box){
49468             panel.setSize(this.box.width, this.box.height);
49469         }
49470         this.fireEvent("panelactivated", this, panel);
49471         this.fireEvent("invalidated");
49472     },
49473     
49474     /**
49475      * Show the specified panel.
49476      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49477      * @return {Roo.ContentPanel} The shown panel or null
49478      */
49479     showPanel : function(panel){
49480         if(panel = this.getPanel(panel)){
49481             this.setActivePanel(panel);
49482         }
49483         return panel;
49484     },
49485     
49486     /**
49487      * Get the active panel for this region.
49488      * @return {Roo.ContentPanel} The active panel or null
49489      */
49490     getActivePanel : function(){
49491         return this.activePanel;
49492     },
49493     
49494     /**
49495      * Add the passed ContentPanel(s)
49496      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49497      * @return {Roo.ContentPanel} The panel added (if only one was added)
49498      */
49499     add : function(panel){
49500         if(arguments.length > 1){
49501             for(var i = 0, len = arguments.length; i < len; i++) {
49502                 this.add(arguments[i]);
49503             }
49504             return null;
49505         }
49506         if(this.hasPanel(panel)){
49507             this.showPanel(panel);
49508             return panel;
49509         }
49510         var el = panel.getEl();
49511         if(el.dom.parentNode != this.mgr.el.dom){
49512             this.mgr.el.dom.appendChild(el.dom);
49513         }
49514         if(panel.setRegion){
49515             panel.setRegion(this);
49516         }
49517         this.panels.add(panel);
49518         el.setStyle("position", "absolute");
49519         if(!panel.background){
49520             this.setActivePanel(panel);
49521             if(this.config.initialSize && this.panels.getCount()==1){
49522                 this.resizeTo(this.config.initialSize);
49523             }
49524         }
49525         this.fireEvent("paneladded", this, panel);
49526         return panel;
49527     },
49528     
49529     /**
49530      * Returns true if the panel is in this region.
49531      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49532      * @return {Boolean}
49533      */
49534     hasPanel : function(panel){
49535         if(typeof panel == "object"){ // must be panel obj
49536             panel = panel.getId();
49537         }
49538         return this.getPanel(panel) ? true : false;
49539     },
49540     
49541     /**
49542      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49543      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49544      * @param {Boolean} preservePanel Overrides the config preservePanel option
49545      * @return {Roo.ContentPanel} The panel that was removed
49546      */
49547     remove : function(panel, preservePanel){
49548         panel = this.getPanel(panel);
49549         if(!panel){
49550             return null;
49551         }
49552         var e = {};
49553         this.fireEvent("beforeremove", this, panel, e);
49554         if(e.cancel === true){
49555             return null;
49556         }
49557         var panelId = panel.getId();
49558         this.panels.removeKey(panelId);
49559         return panel;
49560     },
49561     
49562     /**
49563      * Returns the panel specified or null if it's not in this region.
49564      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49565      * @return {Roo.ContentPanel}
49566      */
49567     getPanel : function(id){
49568         if(typeof id == "object"){ // must be panel obj
49569             return id;
49570         }
49571         return this.panels.get(id);
49572     },
49573     
49574     /**
49575      * Returns this regions position (north/south/east/west/center).
49576      * @return {String} 
49577      */
49578     getPosition: function(){
49579         return this.position;    
49580     }
49581 });/*
49582  * Based on:
49583  * Ext JS Library 1.1.1
49584  * Copyright(c) 2006-2007, Ext JS, LLC.
49585  *
49586  * Originally Released Under LGPL - original licence link has changed is not relivant.
49587  *
49588  * Fork - LGPL
49589  * <script type="text/javascript">
49590  */
49591  
49592 /**
49593  * @class Roo.LayoutRegion
49594  * @extends Roo.BasicLayoutRegion
49595  * This class represents a region in a layout manager.
49596  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49597  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49598  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49599  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49600  * @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})
49601  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49602  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49603  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49604  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49605  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49606  * @cfg {String}    title           The title for the region (overrides panel titles)
49607  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49608  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49609  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49610  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49611  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49612  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49613  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49614  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49615  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49616  * @cfg {Boolean}   showPin         True to show a pin button
49617  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49618  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49619  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49620  * @cfg {Number}    width           For East/West panels
49621  * @cfg {Number}    height          For North/South panels
49622  * @cfg {Boolean}   split           To show the splitter
49623  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49624  */
49625 Roo.LayoutRegion = function(mgr, config, pos){
49626     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49627     var dh = Roo.DomHelper;
49628     /** This region's container element 
49629     * @type Roo.Element */
49630     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49631     /** This region's title element 
49632     * @type Roo.Element */
49633
49634     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49635         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49636         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49637     ]}, true);
49638     this.titleEl.enableDisplayMode();
49639     /** This region's title text element 
49640     * @type HTMLElement */
49641     this.titleTextEl = this.titleEl.dom.firstChild;
49642     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49643     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49644     this.closeBtn.enableDisplayMode();
49645     this.closeBtn.on("click", this.closeClicked, this);
49646     this.closeBtn.hide();
49647
49648     this.createBody(config);
49649     this.visible = true;
49650     this.collapsed = false;
49651
49652     if(config.hideWhenEmpty){
49653         this.hide();
49654         this.on("paneladded", this.validateVisibility, this);
49655         this.on("panelremoved", this.validateVisibility, this);
49656     }
49657     this.applyConfig(config);
49658 };
49659
49660 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49661
49662     createBody : function(){
49663         /** This region's body element 
49664         * @type Roo.Element */
49665         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49666     },
49667
49668     applyConfig : function(c){
49669         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49670             var dh = Roo.DomHelper;
49671             if(c.titlebar !== false){
49672                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49673                 this.collapseBtn.on("click", this.collapse, this);
49674                 this.collapseBtn.enableDisplayMode();
49675
49676                 if(c.showPin === true || this.showPin){
49677                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49678                     this.stickBtn.enableDisplayMode();
49679                     this.stickBtn.on("click", this.expand, this);
49680                     this.stickBtn.hide();
49681                 }
49682             }
49683             /** This region's collapsed element
49684             * @type Roo.Element */
49685             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49686                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49687             ]}, true);
49688             if(c.floatable !== false){
49689                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49690                this.collapsedEl.on("click", this.collapseClick, this);
49691             }
49692
49693             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49694                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49695                    id: "message", unselectable: "on", style:{"float":"left"}});
49696                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49697              }
49698             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49699             this.expandBtn.on("click", this.expand, this);
49700         }
49701         if(this.collapseBtn){
49702             this.collapseBtn.setVisible(c.collapsible == true);
49703         }
49704         this.cmargins = c.cmargins || this.cmargins ||
49705                          (this.position == "west" || this.position == "east" ?
49706                              {top: 0, left: 2, right:2, bottom: 0} :
49707                              {top: 2, left: 0, right:0, bottom: 2});
49708         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49709         this.bottomTabs = c.tabPosition != "top";
49710         this.autoScroll = c.autoScroll || false;
49711         if(this.autoScroll){
49712             this.bodyEl.setStyle("overflow", "auto");
49713         }else{
49714             this.bodyEl.setStyle("overflow", "hidden");
49715         }
49716         //if(c.titlebar !== false){
49717             if((!c.titlebar && !c.title) || c.titlebar === false){
49718                 this.titleEl.hide();
49719             }else{
49720                 this.titleEl.show();
49721                 if(c.title){
49722                     this.titleTextEl.innerHTML = c.title;
49723                 }
49724             }
49725         //}
49726         this.duration = c.duration || .30;
49727         this.slideDuration = c.slideDuration || .45;
49728         this.config = c;
49729         if(c.collapsed){
49730             this.collapse(true);
49731         }
49732         if(c.hidden){
49733             this.hide();
49734         }
49735     },
49736     /**
49737      * Returns true if this region is currently visible.
49738      * @return {Boolean}
49739      */
49740     isVisible : function(){
49741         return this.visible;
49742     },
49743
49744     /**
49745      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49746      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49747      */
49748     setCollapsedTitle : function(title){
49749         title = title || "&#160;";
49750         if(this.collapsedTitleTextEl){
49751             this.collapsedTitleTextEl.innerHTML = title;
49752         }
49753     },
49754
49755     getBox : function(){
49756         var b;
49757         if(!this.collapsed){
49758             b = this.el.getBox(false, true);
49759         }else{
49760             b = this.collapsedEl.getBox(false, true);
49761         }
49762         return b;
49763     },
49764
49765     getMargins : function(){
49766         return this.collapsed ? this.cmargins : this.margins;
49767     },
49768
49769     highlight : function(){
49770         this.el.addClass("x-layout-panel-dragover");
49771     },
49772
49773     unhighlight : function(){
49774         this.el.removeClass("x-layout-panel-dragover");
49775     },
49776
49777     updateBox : function(box){
49778         this.box = box;
49779         if(!this.collapsed){
49780             this.el.dom.style.left = box.x + "px";
49781             this.el.dom.style.top = box.y + "px";
49782             this.updateBody(box.width, box.height);
49783         }else{
49784             this.collapsedEl.dom.style.left = box.x + "px";
49785             this.collapsedEl.dom.style.top = box.y + "px";
49786             this.collapsedEl.setSize(box.width, box.height);
49787         }
49788         if(this.tabs){
49789             this.tabs.autoSizeTabs();
49790         }
49791     },
49792
49793     updateBody : function(w, h){
49794         if(w !== null){
49795             this.el.setWidth(w);
49796             w -= this.el.getBorderWidth("rl");
49797             if(this.config.adjustments){
49798                 w += this.config.adjustments[0];
49799             }
49800         }
49801         if(h !== null){
49802             this.el.setHeight(h);
49803             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49804             h -= this.el.getBorderWidth("tb");
49805             if(this.config.adjustments){
49806                 h += this.config.adjustments[1];
49807             }
49808             this.bodyEl.setHeight(h);
49809             if(this.tabs){
49810                 h = this.tabs.syncHeight(h);
49811             }
49812         }
49813         if(this.panelSize){
49814             w = w !== null ? w : this.panelSize.width;
49815             h = h !== null ? h : this.panelSize.height;
49816         }
49817         if(this.activePanel){
49818             var el = this.activePanel.getEl();
49819             w = w !== null ? w : el.getWidth();
49820             h = h !== null ? h : el.getHeight();
49821             this.panelSize = {width: w, height: h};
49822             this.activePanel.setSize(w, h);
49823         }
49824         if(Roo.isIE && this.tabs){
49825             this.tabs.el.repaint();
49826         }
49827     },
49828
49829     /**
49830      * Returns the container element for this region.
49831      * @return {Roo.Element}
49832      */
49833     getEl : function(){
49834         return this.el;
49835     },
49836
49837     /**
49838      * Hides this region.
49839      */
49840     hide : function(){
49841         if(!this.collapsed){
49842             this.el.dom.style.left = "-2000px";
49843             this.el.hide();
49844         }else{
49845             this.collapsedEl.dom.style.left = "-2000px";
49846             this.collapsedEl.hide();
49847         }
49848         this.visible = false;
49849         this.fireEvent("visibilitychange", this, false);
49850     },
49851
49852     /**
49853      * Shows this region if it was previously hidden.
49854      */
49855     show : function(){
49856         if(!this.collapsed){
49857             this.el.show();
49858         }else{
49859             this.collapsedEl.show();
49860         }
49861         this.visible = true;
49862         this.fireEvent("visibilitychange", this, true);
49863     },
49864
49865     closeClicked : function(){
49866         if(this.activePanel){
49867             this.remove(this.activePanel);
49868         }
49869     },
49870
49871     collapseClick : function(e){
49872         if(this.isSlid){
49873            e.stopPropagation();
49874            this.slideIn();
49875         }else{
49876            e.stopPropagation();
49877            this.slideOut();
49878         }
49879     },
49880
49881     /**
49882      * Collapses this region.
49883      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49884      */
49885     collapse : function(skipAnim){
49886         if(this.collapsed) return;
49887         this.collapsed = true;
49888         if(this.split){
49889             this.split.el.hide();
49890         }
49891         if(this.config.animate && skipAnim !== true){
49892             this.fireEvent("invalidated", this);
49893             this.animateCollapse();
49894         }else{
49895             this.el.setLocation(-20000,-20000);
49896             this.el.hide();
49897             this.collapsedEl.show();
49898             this.fireEvent("collapsed", this);
49899             this.fireEvent("invalidated", this);
49900         }
49901     },
49902
49903     animateCollapse : function(){
49904         // overridden
49905     },
49906
49907     /**
49908      * Expands this region if it was previously collapsed.
49909      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49910      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49911      */
49912     expand : function(e, skipAnim){
49913         if(e) e.stopPropagation();
49914         if(!this.collapsed || this.el.hasActiveFx()) return;
49915         if(this.isSlid){
49916             this.afterSlideIn();
49917             skipAnim = true;
49918         }
49919         this.collapsed = false;
49920         if(this.config.animate && skipAnim !== true){
49921             this.animateExpand();
49922         }else{
49923             this.el.show();
49924             if(this.split){
49925                 this.split.el.show();
49926             }
49927             this.collapsedEl.setLocation(-2000,-2000);
49928             this.collapsedEl.hide();
49929             this.fireEvent("invalidated", this);
49930             this.fireEvent("expanded", this);
49931         }
49932     },
49933
49934     animateExpand : function(){
49935         // overridden
49936     },
49937
49938     initTabs : function()
49939     {
49940         this.bodyEl.setStyle("overflow", "hidden");
49941         var ts = new Roo.TabPanel(
49942                 this.bodyEl.dom,
49943                 {
49944                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49945                     disableTooltips: this.config.disableTabTips,
49946                     toolbar : this.config.toolbar
49947                 }
49948         );
49949         if(this.config.hideTabs){
49950             ts.stripWrap.setDisplayed(false);
49951         }
49952         this.tabs = ts;
49953         ts.resizeTabs = this.config.resizeTabs === true;
49954         ts.minTabWidth = this.config.minTabWidth || 40;
49955         ts.maxTabWidth = this.config.maxTabWidth || 250;
49956         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49957         ts.monitorResize = false;
49958         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49959         ts.bodyEl.addClass('x-layout-tabs-body');
49960         this.panels.each(this.initPanelAsTab, this);
49961     },
49962
49963     initPanelAsTab : function(panel){
49964         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49965                     this.config.closeOnTab && panel.isClosable());
49966         if(panel.tabTip !== undefined){
49967             ti.setTooltip(panel.tabTip);
49968         }
49969         ti.on("activate", function(){
49970               this.setActivePanel(panel);
49971         }, this);
49972         if(this.config.closeOnTab){
49973             ti.on("beforeclose", function(t, e){
49974                 e.cancel = true;
49975                 this.remove(panel);
49976             }, this);
49977         }
49978         return ti;
49979     },
49980
49981     updatePanelTitle : function(panel, title){
49982         if(this.activePanel == panel){
49983             this.updateTitle(title);
49984         }
49985         if(this.tabs){
49986             var ti = this.tabs.getTab(panel.getEl().id);
49987             ti.setText(title);
49988             if(panel.tabTip !== undefined){
49989                 ti.setTooltip(panel.tabTip);
49990             }
49991         }
49992     },
49993
49994     updateTitle : function(title){
49995         if(this.titleTextEl && !this.config.title){
49996             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49997         }
49998     },
49999
50000     setActivePanel : function(panel){
50001         panel = this.getPanel(panel);
50002         if(this.activePanel && this.activePanel != panel){
50003             this.activePanel.setActiveState(false);
50004         }
50005         this.activePanel = panel;
50006         panel.setActiveState(true);
50007         if(this.panelSize){
50008             panel.setSize(this.panelSize.width, this.panelSize.height);
50009         }
50010         if(this.closeBtn){
50011             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50012         }
50013         this.updateTitle(panel.getTitle());
50014         if(this.tabs){
50015             this.fireEvent("invalidated", this);
50016         }
50017         this.fireEvent("panelactivated", this, panel);
50018     },
50019
50020     /**
50021      * Shows the specified panel.
50022      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50023      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50024      */
50025     showPanel : function(panel){
50026         if(panel = this.getPanel(panel)){
50027             if(this.tabs){
50028                 var tab = this.tabs.getTab(panel.getEl().id);
50029                 if(tab.isHidden()){
50030                     this.tabs.unhideTab(tab.id);
50031                 }
50032                 tab.activate();
50033             }else{
50034                 this.setActivePanel(panel);
50035             }
50036         }
50037         return panel;
50038     },
50039
50040     /**
50041      * Get the active panel for this region.
50042      * @return {Roo.ContentPanel} The active panel or null
50043      */
50044     getActivePanel : function(){
50045         return this.activePanel;
50046     },
50047
50048     validateVisibility : function(){
50049         if(this.panels.getCount() < 1){
50050             this.updateTitle("&#160;");
50051             this.closeBtn.hide();
50052             this.hide();
50053         }else{
50054             if(!this.isVisible()){
50055                 this.show();
50056             }
50057         }
50058     },
50059
50060     /**
50061      * Adds the passed ContentPanel(s) to this region.
50062      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50063      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50064      */
50065     add : function(panel){
50066         if(arguments.length > 1){
50067             for(var i = 0, len = arguments.length; i < len; i++) {
50068                 this.add(arguments[i]);
50069             }
50070             return null;
50071         }
50072         if(this.hasPanel(panel)){
50073             this.showPanel(panel);
50074             return panel;
50075         }
50076         panel.setRegion(this);
50077         this.panels.add(panel);
50078         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50079             this.bodyEl.dom.appendChild(panel.getEl().dom);
50080             if(panel.background !== true){
50081                 this.setActivePanel(panel);
50082             }
50083             this.fireEvent("paneladded", this, panel);
50084             return panel;
50085         }
50086         if(!this.tabs){
50087             this.initTabs();
50088         }else{
50089             this.initPanelAsTab(panel);
50090         }
50091         if(panel.background !== true){
50092             this.tabs.activate(panel.getEl().id);
50093         }
50094         this.fireEvent("paneladded", this, panel);
50095         return panel;
50096     },
50097
50098     /**
50099      * Hides the tab for the specified panel.
50100      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50101      */
50102     hidePanel : function(panel){
50103         if(this.tabs && (panel = this.getPanel(panel))){
50104             this.tabs.hideTab(panel.getEl().id);
50105         }
50106     },
50107
50108     /**
50109      * Unhides the tab for a previously hidden panel.
50110      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50111      */
50112     unhidePanel : function(panel){
50113         if(this.tabs && (panel = this.getPanel(panel))){
50114             this.tabs.unhideTab(panel.getEl().id);
50115         }
50116     },
50117
50118     clearPanels : function(){
50119         while(this.panels.getCount() > 0){
50120              this.remove(this.panels.first());
50121         }
50122     },
50123
50124     /**
50125      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50126      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50127      * @param {Boolean} preservePanel Overrides the config preservePanel option
50128      * @return {Roo.ContentPanel} The panel that was removed
50129      */
50130     remove : function(panel, preservePanel){
50131         panel = this.getPanel(panel);
50132         if(!panel){
50133             return null;
50134         }
50135         var e = {};
50136         this.fireEvent("beforeremove", this, panel, e);
50137         if(e.cancel === true){
50138             return null;
50139         }
50140         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50141         var panelId = panel.getId();
50142         this.panels.removeKey(panelId);
50143         if(preservePanel){
50144             document.body.appendChild(panel.getEl().dom);
50145         }
50146         if(this.tabs){
50147             this.tabs.removeTab(panel.getEl().id);
50148         }else if (!preservePanel){
50149             this.bodyEl.dom.removeChild(panel.getEl().dom);
50150         }
50151         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50152             var p = this.panels.first();
50153             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50154             tempEl.appendChild(p.getEl().dom);
50155             this.bodyEl.update("");
50156             this.bodyEl.dom.appendChild(p.getEl().dom);
50157             tempEl = null;
50158             this.updateTitle(p.getTitle());
50159             this.tabs = null;
50160             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50161             this.setActivePanel(p);
50162         }
50163         panel.setRegion(null);
50164         if(this.activePanel == panel){
50165             this.activePanel = null;
50166         }
50167         if(this.config.autoDestroy !== false && preservePanel !== true){
50168             try{panel.destroy();}catch(e){}
50169         }
50170         this.fireEvent("panelremoved", this, panel);
50171         return panel;
50172     },
50173
50174     /**
50175      * Returns the TabPanel component used by this region
50176      * @return {Roo.TabPanel}
50177      */
50178     getTabs : function(){
50179         return this.tabs;
50180     },
50181
50182     createTool : function(parentEl, className){
50183         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50184             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50185         btn.addClassOnOver("x-layout-tools-button-over");
50186         return btn;
50187     }
50188 });/*
50189  * Based on:
50190  * Ext JS Library 1.1.1
50191  * Copyright(c) 2006-2007, Ext JS, LLC.
50192  *
50193  * Originally Released Under LGPL - original licence link has changed is not relivant.
50194  *
50195  * Fork - LGPL
50196  * <script type="text/javascript">
50197  */
50198  
50199
50200
50201 /**
50202  * @class Roo.SplitLayoutRegion
50203  * @extends Roo.LayoutRegion
50204  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50205  */
50206 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50207     this.cursor = cursor;
50208     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50209 };
50210
50211 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50212     splitTip : "Drag to resize.",
50213     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50214     useSplitTips : false,
50215
50216     applyConfig : function(config){
50217         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50218         if(config.split){
50219             if(!this.split){
50220                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50221                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50222                 /** The SplitBar for this region 
50223                 * @type Roo.SplitBar */
50224                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50225                 this.split.on("moved", this.onSplitMove, this);
50226                 this.split.useShim = config.useShim === true;
50227                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50228                 if(this.useSplitTips){
50229                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50230                 }
50231                 if(config.collapsible){
50232                     this.split.el.on("dblclick", this.collapse,  this);
50233                 }
50234             }
50235             if(typeof config.minSize != "undefined"){
50236                 this.split.minSize = config.minSize;
50237             }
50238             if(typeof config.maxSize != "undefined"){
50239                 this.split.maxSize = config.maxSize;
50240             }
50241             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50242                 this.hideSplitter();
50243             }
50244         }
50245     },
50246
50247     getHMaxSize : function(){
50248          var cmax = this.config.maxSize || 10000;
50249          var center = this.mgr.getRegion("center");
50250          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50251     },
50252
50253     getVMaxSize : function(){
50254          var cmax = this.config.maxSize || 10000;
50255          var center = this.mgr.getRegion("center");
50256          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50257     },
50258
50259     onSplitMove : function(split, newSize){
50260         this.fireEvent("resized", this, newSize);
50261     },
50262     
50263     /** 
50264      * Returns the {@link Roo.SplitBar} for this region.
50265      * @return {Roo.SplitBar}
50266      */
50267     getSplitBar : function(){
50268         return this.split;
50269     },
50270     
50271     hide : function(){
50272         this.hideSplitter();
50273         Roo.SplitLayoutRegion.superclass.hide.call(this);
50274     },
50275
50276     hideSplitter : function(){
50277         if(this.split){
50278             this.split.el.setLocation(-2000,-2000);
50279             this.split.el.hide();
50280         }
50281     },
50282
50283     show : function(){
50284         if(this.split){
50285             this.split.el.show();
50286         }
50287         Roo.SplitLayoutRegion.superclass.show.call(this);
50288     },
50289     
50290     beforeSlide: function(){
50291         if(Roo.isGecko){// firefox overflow auto bug workaround
50292             this.bodyEl.clip();
50293             if(this.tabs) this.tabs.bodyEl.clip();
50294             if(this.activePanel){
50295                 this.activePanel.getEl().clip();
50296                 
50297                 if(this.activePanel.beforeSlide){
50298                     this.activePanel.beforeSlide();
50299                 }
50300             }
50301         }
50302     },
50303     
50304     afterSlide : function(){
50305         if(Roo.isGecko){// firefox overflow auto bug workaround
50306             this.bodyEl.unclip();
50307             if(this.tabs) this.tabs.bodyEl.unclip();
50308             if(this.activePanel){
50309                 this.activePanel.getEl().unclip();
50310                 if(this.activePanel.afterSlide){
50311                     this.activePanel.afterSlide();
50312                 }
50313             }
50314         }
50315     },
50316
50317     initAutoHide : function(){
50318         if(this.autoHide !== false){
50319             if(!this.autoHideHd){
50320                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50321                 this.autoHideHd = {
50322                     "mouseout": function(e){
50323                         if(!e.within(this.el, true)){
50324                             st.delay(500);
50325                         }
50326                     },
50327                     "mouseover" : function(e){
50328                         st.cancel();
50329                     },
50330                     scope : this
50331                 };
50332             }
50333             this.el.on(this.autoHideHd);
50334         }
50335     },
50336
50337     clearAutoHide : function(){
50338         if(this.autoHide !== false){
50339             this.el.un("mouseout", this.autoHideHd.mouseout);
50340             this.el.un("mouseover", this.autoHideHd.mouseover);
50341         }
50342     },
50343
50344     clearMonitor : function(){
50345         Roo.get(document).un("click", this.slideInIf, this);
50346     },
50347
50348     // these names are backwards but not changed for compat
50349     slideOut : function(){
50350         if(this.isSlid || this.el.hasActiveFx()){
50351             return;
50352         }
50353         this.isSlid = true;
50354         if(this.collapseBtn){
50355             this.collapseBtn.hide();
50356         }
50357         this.closeBtnState = this.closeBtn.getStyle('display');
50358         this.closeBtn.hide();
50359         if(this.stickBtn){
50360             this.stickBtn.show();
50361         }
50362         this.el.show();
50363         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50364         this.beforeSlide();
50365         this.el.setStyle("z-index", 10001);
50366         this.el.slideIn(this.getSlideAnchor(), {
50367             callback: function(){
50368                 this.afterSlide();
50369                 this.initAutoHide();
50370                 Roo.get(document).on("click", this.slideInIf, this);
50371                 this.fireEvent("slideshow", this);
50372             },
50373             scope: this,
50374             block: true
50375         });
50376     },
50377
50378     afterSlideIn : function(){
50379         this.clearAutoHide();
50380         this.isSlid = false;
50381         this.clearMonitor();
50382         this.el.setStyle("z-index", "");
50383         if(this.collapseBtn){
50384             this.collapseBtn.show();
50385         }
50386         this.closeBtn.setStyle('display', this.closeBtnState);
50387         if(this.stickBtn){
50388             this.stickBtn.hide();
50389         }
50390         this.fireEvent("slidehide", this);
50391     },
50392
50393     slideIn : function(cb){
50394         if(!this.isSlid || this.el.hasActiveFx()){
50395             Roo.callback(cb);
50396             return;
50397         }
50398         this.isSlid = false;
50399         this.beforeSlide();
50400         this.el.slideOut(this.getSlideAnchor(), {
50401             callback: function(){
50402                 this.el.setLeftTop(-10000, -10000);
50403                 this.afterSlide();
50404                 this.afterSlideIn();
50405                 Roo.callback(cb);
50406             },
50407             scope: this,
50408             block: true
50409         });
50410     },
50411     
50412     slideInIf : function(e){
50413         if(!e.within(this.el)){
50414             this.slideIn();
50415         }
50416     },
50417
50418     animateCollapse : function(){
50419         this.beforeSlide();
50420         this.el.setStyle("z-index", 20000);
50421         var anchor = this.getSlideAnchor();
50422         this.el.slideOut(anchor, {
50423             callback : function(){
50424                 this.el.setStyle("z-index", "");
50425                 this.collapsedEl.slideIn(anchor, {duration:.3});
50426                 this.afterSlide();
50427                 this.el.setLocation(-10000,-10000);
50428                 this.el.hide();
50429                 this.fireEvent("collapsed", this);
50430             },
50431             scope: this,
50432             block: true
50433         });
50434     },
50435
50436     animateExpand : function(){
50437         this.beforeSlide();
50438         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50439         this.el.setStyle("z-index", 20000);
50440         this.collapsedEl.hide({
50441             duration:.1
50442         });
50443         this.el.slideIn(this.getSlideAnchor(), {
50444             callback : function(){
50445                 this.el.setStyle("z-index", "");
50446                 this.afterSlide();
50447                 if(this.split){
50448                     this.split.el.show();
50449                 }
50450                 this.fireEvent("invalidated", this);
50451                 this.fireEvent("expanded", this);
50452             },
50453             scope: this,
50454             block: true
50455         });
50456     },
50457
50458     anchors : {
50459         "west" : "left",
50460         "east" : "right",
50461         "north" : "top",
50462         "south" : "bottom"
50463     },
50464
50465     sanchors : {
50466         "west" : "l",
50467         "east" : "r",
50468         "north" : "t",
50469         "south" : "b"
50470     },
50471
50472     canchors : {
50473         "west" : "tl-tr",
50474         "east" : "tr-tl",
50475         "north" : "tl-bl",
50476         "south" : "bl-tl"
50477     },
50478
50479     getAnchor : function(){
50480         return this.anchors[this.position];
50481     },
50482
50483     getCollapseAnchor : function(){
50484         return this.canchors[this.position];
50485     },
50486
50487     getSlideAnchor : function(){
50488         return this.sanchors[this.position];
50489     },
50490
50491     getAlignAdj : function(){
50492         var cm = this.cmargins;
50493         switch(this.position){
50494             case "west":
50495                 return [0, 0];
50496             break;
50497             case "east":
50498                 return [0, 0];
50499             break;
50500             case "north":
50501                 return [0, 0];
50502             break;
50503             case "south":
50504                 return [0, 0];
50505             break;
50506         }
50507     },
50508
50509     getExpandAdj : function(){
50510         var c = this.collapsedEl, cm = this.cmargins;
50511         switch(this.position){
50512             case "west":
50513                 return [-(cm.right+c.getWidth()+cm.left), 0];
50514             break;
50515             case "east":
50516                 return [cm.right+c.getWidth()+cm.left, 0];
50517             break;
50518             case "north":
50519                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50520             break;
50521             case "south":
50522                 return [0, cm.top+cm.bottom+c.getHeight()];
50523             break;
50524         }
50525     }
50526 });/*
50527  * Based on:
50528  * Ext JS Library 1.1.1
50529  * Copyright(c) 2006-2007, Ext JS, LLC.
50530  *
50531  * Originally Released Under LGPL - original licence link has changed is not relivant.
50532  *
50533  * Fork - LGPL
50534  * <script type="text/javascript">
50535  */
50536 /*
50537  * These classes are private internal classes
50538  */
50539 Roo.CenterLayoutRegion = function(mgr, config){
50540     Roo.LayoutRegion.call(this, mgr, config, "center");
50541     this.visible = true;
50542     this.minWidth = config.minWidth || 20;
50543     this.minHeight = config.minHeight || 20;
50544 };
50545
50546 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50547     hide : function(){
50548         // center panel can't be hidden
50549     },
50550     
50551     show : function(){
50552         // center panel can't be hidden
50553     },
50554     
50555     getMinWidth: function(){
50556         return this.minWidth;
50557     },
50558     
50559     getMinHeight: function(){
50560         return this.minHeight;
50561     }
50562 });
50563
50564
50565 Roo.NorthLayoutRegion = function(mgr, config){
50566     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50567     if(this.split){
50568         this.split.placement = Roo.SplitBar.TOP;
50569         this.split.orientation = Roo.SplitBar.VERTICAL;
50570         this.split.el.addClass("x-layout-split-v");
50571     }
50572     var size = config.initialSize || config.height;
50573     if(typeof size != "undefined"){
50574         this.el.setHeight(size);
50575     }
50576 };
50577 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50578     orientation: Roo.SplitBar.VERTICAL,
50579     getBox : function(){
50580         if(this.collapsed){
50581             return this.collapsedEl.getBox();
50582         }
50583         var box = this.el.getBox();
50584         if(this.split){
50585             box.height += this.split.el.getHeight();
50586         }
50587         return box;
50588     },
50589     
50590     updateBox : function(box){
50591         if(this.split && !this.collapsed){
50592             box.height -= this.split.el.getHeight();
50593             this.split.el.setLeft(box.x);
50594             this.split.el.setTop(box.y+box.height);
50595             this.split.el.setWidth(box.width);
50596         }
50597         if(this.collapsed){
50598             this.updateBody(box.width, null);
50599         }
50600         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50601     }
50602 });
50603
50604 Roo.SouthLayoutRegion = function(mgr, config){
50605     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50606     if(this.split){
50607         this.split.placement = Roo.SplitBar.BOTTOM;
50608         this.split.orientation = Roo.SplitBar.VERTICAL;
50609         this.split.el.addClass("x-layout-split-v");
50610     }
50611     var size = config.initialSize || config.height;
50612     if(typeof size != "undefined"){
50613         this.el.setHeight(size);
50614     }
50615 };
50616 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50617     orientation: Roo.SplitBar.VERTICAL,
50618     getBox : function(){
50619         if(this.collapsed){
50620             return this.collapsedEl.getBox();
50621         }
50622         var box = this.el.getBox();
50623         if(this.split){
50624             var sh = this.split.el.getHeight();
50625             box.height += sh;
50626             box.y -= sh;
50627         }
50628         return box;
50629     },
50630     
50631     updateBox : function(box){
50632         if(this.split && !this.collapsed){
50633             var sh = this.split.el.getHeight();
50634             box.height -= sh;
50635             box.y += sh;
50636             this.split.el.setLeft(box.x);
50637             this.split.el.setTop(box.y-sh);
50638             this.split.el.setWidth(box.width);
50639         }
50640         if(this.collapsed){
50641             this.updateBody(box.width, null);
50642         }
50643         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50644     }
50645 });
50646
50647 Roo.EastLayoutRegion = function(mgr, config){
50648     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50649     if(this.split){
50650         this.split.placement = Roo.SplitBar.RIGHT;
50651         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50652         this.split.el.addClass("x-layout-split-h");
50653     }
50654     var size = config.initialSize || config.width;
50655     if(typeof size != "undefined"){
50656         this.el.setWidth(size);
50657     }
50658 };
50659 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50660     orientation: Roo.SplitBar.HORIZONTAL,
50661     getBox : function(){
50662         if(this.collapsed){
50663             return this.collapsedEl.getBox();
50664         }
50665         var box = this.el.getBox();
50666         if(this.split){
50667             var sw = this.split.el.getWidth();
50668             box.width += sw;
50669             box.x -= sw;
50670         }
50671         return box;
50672     },
50673
50674     updateBox : function(box){
50675         if(this.split && !this.collapsed){
50676             var sw = this.split.el.getWidth();
50677             box.width -= sw;
50678             this.split.el.setLeft(box.x);
50679             this.split.el.setTop(box.y);
50680             this.split.el.setHeight(box.height);
50681             box.x += sw;
50682         }
50683         if(this.collapsed){
50684             this.updateBody(null, box.height);
50685         }
50686         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50687     }
50688 });
50689
50690 Roo.WestLayoutRegion = function(mgr, config){
50691     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50692     if(this.split){
50693         this.split.placement = Roo.SplitBar.LEFT;
50694         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50695         this.split.el.addClass("x-layout-split-h");
50696     }
50697     var size = config.initialSize || config.width;
50698     if(typeof size != "undefined"){
50699         this.el.setWidth(size);
50700     }
50701 };
50702 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50703     orientation: Roo.SplitBar.HORIZONTAL,
50704     getBox : function(){
50705         if(this.collapsed){
50706             return this.collapsedEl.getBox();
50707         }
50708         var box = this.el.getBox();
50709         if(this.split){
50710             box.width += this.split.el.getWidth();
50711         }
50712         return box;
50713     },
50714     
50715     updateBox : function(box){
50716         if(this.split && !this.collapsed){
50717             var sw = this.split.el.getWidth();
50718             box.width -= sw;
50719             this.split.el.setLeft(box.x+box.width);
50720             this.split.el.setTop(box.y);
50721             this.split.el.setHeight(box.height);
50722         }
50723         if(this.collapsed){
50724             this.updateBody(null, box.height);
50725         }
50726         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50727     }
50728 });
50729 /*
50730  * Based on:
50731  * Ext JS Library 1.1.1
50732  * Copyright(c) 2006-2007, Ext JS, LLC.
50733  *
50734  * Originally Released Under LGPL - original licence link has changed is not relivant.
50735  *
50736  * Fork - LGPL
50737  * <script type="text/javascript">
50738  */
50739  
50740  
50741 /*
50742  * Private internal class for reading and applying state
50743  */
50744 Roo.LayoutStateManager = function(layout){
50745      // default empty state
50746      this.state = {
50747         north: {},
50748         south: {},
50749         east: {},
50750         west: {}       
50751     };
50752 };
50753
50754 Roo.LayoutStateManager.prototype = {
50755     init : function(layout, provider){
50756         this.provider = provider;
50757         var state = provider.get(layout.id+"-layout-state");
50758         if(state){
50759             var wasUpdating = layout.isUpdating();
50760             if(!wasUpdating){
50761                 layout.beginUpdate();
50762             }
50763             for(var key in state){
50764                 if(typeof state[key] != "function"){
50765                     var rstate = state[key];
50766                     var r = layout.getRegion(key);
50767                     if(r && rstate){
50768                         if(rstate.size){
50769                             r.resizeTo(rstate.size);
50770                         }
50771                         if(rstate.collapsed == true){
50772                             r.collapse(true);
50773                         }else{
50774                             r.expand(null, true);
50775                         }
50776                     }
50777                 }
50778             }
50779             if(!wasUpdating){
50780                 layout.endUpdate();
50781             }
50782             this.state = state; 
50783         }
50784         this.layout = layout;
50785         layout.on("regionresized", this.onRegionResized, this);
50786         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50787         layout.on("regionexpanded", this.onRegionExpanded, this);
50788     },
50789     
50790     storeState : function(){
50791         this.provider.set(this.layout.id+"-layout-state", this.state);
50792     },
50793     
50794     onRegionResized : function(region, newSize){
50795         this.state[region.getPosition()].size = newSize;
50796         this.storeState();
50797     },
50798     
50799     onRegionCollapsed : function(region){
50800         this.state[region.getPosition()].collapsed = true;
50801         this.storeState();
50802     },
50803     
50804     onRegionExpanded : function(region){
50805         this.state[region.getPosition()].collapsed = false;
50806         this.storeState();
50807     }
50808 };/*
50809  * Based on:
50810  * Ext JS Library 1.1.1
50811  * Copyright(c) 2006-2007, Ext JS, LLC.
50812  *
50813  * Originally Released Under LGPL - original licence link has changed is not relivant.
50814  *
50815  * Fork - LGPL
50816  * <script type="text/javascript">
50817  */
50818 /**
50819  * @class Roo.ContentPanel
50820  * @extends Roo.util.Observable
50821  * A basic ContentPanel element.
50822  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50823  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50824  * @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
50825  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50826  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50827  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50828  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50829  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50830  * @cfg {String} title          The title for this panel
50831  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50832  * @cfg {String} url            Calls {@link #setUrl} with this value
50833  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50834  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50835  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50836  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50837
50838  * @constructor
50839  * Create a new ContentPanel.
50840  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50841  * @param {String/Object} config A string to set only the title or a config object
50842  * @param {String} content (optional) Set the HTML content for this panel
50843  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50844  */
50845 Roo.ContentPanel = function(el, config, content){
50846     
50847      
50848     /*
50849     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50850         config = el;
50851         el = Roo.id();
50852     }
50853     if (config && config.parentLayout) { 
50854         el = config.parentLayout.el.createChild(); 
50855     }
50856     */
50857     if(el.autoCreate){ // xtype is available if this is called from factory
50858         config = el;
50859         el = Roo.id();
50860     }
50861     this.el = Roo.get(el);
50862     if(!this.el && config && config.autoCreate){
50863         if(typeof config.autoCreate == "object"){
50864             if(!config.autoCreate.id){
50865                 config.autoCreate.id = config.id||el;
50866             }
50867             this.el = Roo.DomHelper.append(document.body,
50868                         config.autoCreate, true);
50869         }else{
50870             this.el = Roo.DomHelper.append(document.body,
50871                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50872         }
50873     }
50874     this.closable = false;
50875     this.loaded = false;
50876     this.active = false;
50877     if(typeof config == "string"){
50878         this.title = config;
50879     }else{
50880         Roo.apply(this, config);
50881     }
50882     
50883     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50884         this.wrapEl = this.el.wrap();
50885         this.toolbar.container = this.el.insertSibling(false, 'before');
50886         this.toolbar = new Roo.Toolbar(this.toolbar);
50887     }
50888     
50889     // xtype created footer. - not sure if will work as we normally have to render first..
50890     if (this.footer && !this.footer.el && this.footer.xtype) {
50891         if (!this.wrapEl) {
50892             this.wrapEl = this.el.wrap();
50893         }
50894     
50895         this.footer.container = this.wrapEl.createChild();
50896          
50897         this.footer = Roo.factory(this.footer, Roo);
50898         
50899     }
50900     
50901     if(this.resizeEl){
50902         this.resizeEl = Roo.get(this.resizeEl, true);
50903     }else{
50904         this.resizeEl = this.el;
50905     }
50906     // handle view.xtype
50907     
50908  
50909     
50910     
50911     this.addEvents({
50912         /**
50913          * @event activate
50914          * Fires when this panel is activated. 
50915          * @param {Roo.ContentPanel} this
50916          */
50917         "activate" : true,
50918         /**
50919          * @event deactivate
50920          * Fires when this panel is activated. 
50921          * @param {Roo.ContentPanel} this
50922          */
50923         "deactivate" : true,
50924
50925         /**
50926          * @event resize
50927          * Fires when this panel is resized if fitToFrame is true.
50928          * @param {Roo.ContentPanel} this
50929          * @param {Number} width The width after any component adjustments
50930          * @param {Number} height The height after any component adjustments
50931          */
50932         "resize" : true,
50933         
50934          /**
50935          * @event render
50936          * Fires when this tab is created
50937          * @param {Roo.ContentPanel} this
50938          */
50939         "render" : true
50940         
50941         
50942         
50943     });
50944     
50945
50946     
50947     
50948     if(this.autoScroll){
50949         this.resizeEl.setStyle("overflow", "auto");
50950     } else {
50951         // fix randome scrolling
50952         this.el.on('scroll', function() {
50953             Roo.log('fix random scolling');
50954             this.scrollTo('top',0); 
50955         });
50956     }
50957     content = content || this.content;
50958     if(content){
50959         this.setContent(content);
50960     }
50961     if(config && config.url){
50962         this.setUrl(this.url, this.params, this.loadOnce);
50963     }
50964     
50965     
50966     
50967     Roo.ContentPanel.superclass.constructor.call(this);
50968     
50969     if (this.view && typeof(this.view.xtype) != 'undefined') {
50970         this.view.el = this.el.appendChild(document.createElement("div"));
50971         this.view = Roo.factory(this.view); 
50972         this.view.render  &&  this.view.render(false, '');  
50973     }
50974     
50975     
50976     this.fireEvent('render', this);
50977 };
50978
50979 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50980     tabTip:'',
50981     setRegion : function(region){
50982         this.region = region;
50983         if(region){
50984            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50985         }else{
50986            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50987         } 
50988     },
50989     
50990     /**
50991      * Returns the toolbar for this Panel if one was configured. 
50992      * @return {Roo.Toolbar} 
50993      */
50994     getToolbar : function(){
50995         return this.toolbar;
50996     },
50997     
50998     setActiveState : function(active){
50999         this.active = active;
51000         if(!active){
51001             this.fireEvent("deactivate", this);
51002         }else{
51003             this.fireEvent("activate", this);
51004         }
51005     },
51006     /**
51007      * Updates this panel's element
51008      * @param {String} content The new content
51009      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51010     */
51011     setContent : function(content, loadScripts){
51012         this.el.update(content, loadScripts);
51013     },
51014
51015     ignoreResize : function(w, h){
51016         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51017             return true;
51018         }else{
51019             this.lastSize = {width: w, height: h};
51020             return false;
51021         }
51022     },
51023     /**
51024      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51025      * @return {Roo.UpdateManager} The UpdateManager
51026      */
51027     getUpdateManager : function(){
51028         return this.el.getUpdateManager();
51029     },
51030      /**
51031      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51032      * @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:
51033 <pre><code>
51034 panel.load({
51035     url: "your-url.php",
51036     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51037     callback: yourFunction,
51038     scope: yourObject, //(optional scope)
51039     discardUrl: false,
51040     nocache: false,
51041     text: "Loading...",
51042     timeout: 30,
51043     scripts: false
51044 });
51045 </code></pre>
51046      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51047      * 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.
51048      * @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}
51049      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51050      * @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.
51051      * @return {Roo.ContentPanel} this
51052      */
51053     load : function(){
51054         var um = this.el.getUpdateManager();
51055         um.update.apply(um, arguments);
51056         return this;
51057     },
51058
51059
51060     /**
51061      * 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.
51062      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51063      * @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)
51064      * @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)
51065      * @return {Roo.UpdateManager} The UpdateManager
51066      */
51067     setUrl : function(url, params, loadOnce){
51068         if(this.refreshDelegate){
51069             this.removeListener("activate", this.refreshDelegate);
51070         }
51071         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51072         this.on("activate", this.refreshDelegate);
51073         return this.el.getUpdateManager();
51074     },
51075     
51076     _handleRefresh : function(url, params, loadOnce){
51077         if(!loadOnce || !this.loaded){
51078             var updater = this.el.getUpdateManager();
51079             updater.update(url, params, this._setLoaded.createDelegate(this));
51080         }
51081     },
51082     
51083     _setLoaded : function(){
51084         this.loaded = true;
51085     }, 
51086     
51087     /**
51088      * Returns this panel's id
51089      * @return {String} 
51090      */
51091     getId : function(){
51092         return this.el.id;
51093     },
51094     
51095     /** 
51096      * Returns this panel's element - used by regiosn to add.
51097      * @return {Roo.Element} 
51098      */
51099     getEl : function(){
51100         return this.wrapEl || this.el;
51101     },
51102     
51103     adjustForComponents : function(width, height)
51104     {
51105         //Roo.log('adjustForComponents ');
51106         if(this.resizeEl != this.el){
51107             width -= this.el.getFrameWidth('lr');
51108             height -= this.el.getFrameWidth('tb');
51109         }
51110         if(this.toolbar){
51111             var te = this.toolbar.getEl();
51112             height -= te.getHeight();
51113             te.setWidth(width);
51114         }
51115         if(this.footer){
51116             var te = this.footer.getEl();
51117             Roo.log("footer:" + te.getHeight());
51118             
51119             height -= te.getHeight();
51120             te.setWidth(width);
51121         }
51122         
51123         
51124         if(this.adjustments){
51125             width += this.adjustments[0];
51126             height += this.adjustments[1];
51127         }
51128         return {"width": width, "height": height};
51129     },
51130     
51131     setSize : function(width, height){
51132         if(this.fitToFrame && !this.ignoreResize(width, height)){
51133             if(this.fitContainer && this.resizeEl != this.el){
51134                 this.el.setSize(width, height);
51135             }
51136             var size = this.adjustForComponents(width, height);
51137             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51138             this.fireEvent('resize', this, size.width, size.height);
51139         }
51140     },
51141     
51142     /**
51143      * Returns this panel's title
51144      * @return {String} 
51145      */
51146     getTitle : function(){
51147         return this.title;
51148     },
51149     
51150     /**
51151      * Set this panel's title
51152      * @param {String} title
51153      */
51154     setTitle : function(title){
51155         this.title = title;
51156         if(this.region){
51157             this.region.updatePanelTitle(this, title);
51158         }
51159     },
51160     
51161     /**
51162      * Returns true is this panel was configured to be closable
51163      * @return {Boolean} 
51164      */
51165     isClosable : function(){
51166         return this.closable;
51167     },
51168     
51169     beforeSlide : function(){
51170         this.el.clip();
51171         this.resizeEl.clip();
51172     },
51173     
51174     afterSlide : function(){
51175         this.el.unclip();
51176         this.resizeEl.unclip();
51177     },
51178     
51179     /**
51180      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51181      *   Will fail silently if the {@link #setUrl} method has not been called.
51182      *   This does not activate the panel, just updates its content.
51183      */
51184     refresh : function(){
51185         if(this.refreshDelegate){
51186            this.loaded = false;
51187            this.refreshDelegate();
51188         }
51189     },
51190     
51191     /**
51192      * Destroys this panel
51193      */
51194     destroy : function(){
51195         this.el.removeAllListeners();
51196         var tempEl = document.createElement("span");
51197         tempEl.appendChild(this.el.dom);
51198         tempEl.innerHTML = "";
51199         this.el.remove();
51200         this.el = null;
51201     },
51202     
51203     /**
51204      * form - if the content panel contains a form - this is a reference to it.
51205      * @type {Roo.form.Form}
51206      */
51207     form : false,
51208     /**
51209      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51210      *    This contains a reference to it.
51211      * @type {Roo.View}
51212      */
51213     view : false,
51214     
51215       /**
51216      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51217      * <pre><code>
51218
51219 layout.addxtype({
51220        xtype : 'Form',
51221        items: [ .... ]
51222    }
51223 );
51224
51225 </code></pre>
51226      * @param {Object} cfg Xtype definition of item to add.
51227      */
51228     
51229     addxtype : function(cfg) {
51230         // add form..
51231         if (cfg.xtype.match(/^Form$/)) {
51232             
51233             var el;
51234             //if (this.footer) {
51235             //    el = this.footer.container.insertSibling(false, 'before');
51236             //} else {
51237                 el = this.el.createChild();
51238             //}
51239
51240             this.form = new  Roo.form.Form(cfg);
51241             
51242             
51243             if ( this.form.allItems.length) this.form.render(el.dom);
51244             return this.form;
51245         }
51246         // should only have one of theses..
51247         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51248             // views.. should not be just added - used named prop 'view''
51249             
51250             cfg.el = this.el.appendChild(document.createElement("div"));
51251             // factory?
51252             
51253             var ret = new Roo.factory(cfg);
51254              
51255              ret.render && ret.render(false, ''); // render blank..
51256             this.view = ret;
51257             return ret;
51258         }
51259         return false;
51260     }
51261 });
51262
51263 /**
51264  * @class Roo.GridPanel
51265  * @extends Roo.ContentPanel
51266  * @constructor
51267  * Create a new GridPanel.
51268  * @param {Roo.grid.Grid} grid The grid for this panel
51269  * @param {String/Object} config A string to set only the panel's title, or a config object
51270  */
51271 Roo.GridPanel = function(grid, config){
51272     
51273   
51274     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51275         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51276         
51277     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51278     
51279     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51280     
51281     if(this.toolbar){
51282         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51283     }
51284     // xtype created footer. - not sure if will work as we normally have to render first..
51285     if (this.footer && !this.footer.el && this.footer.xtype) {
51286         
51287         this.footer.container = this.grid.getView().getFooterPanel(true);
51288         this.footer.dataSource = this.grid.dataSource;
51289         this.footer = Roo.factory(this.footer, Roo);
51290         
51291     }
51292     
51293     grid.monitorWindowResize = false; // turn off autosizing
51294     grid.autoHeight = false;
51295     grid.autoWidth = false;
51296     this.grid = grid;
51297     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51298 };
51299
51300 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51301     getId : function(){
51302         return this.grid.id;
51303     },
51304     
51305     /**
51306      * Returns the grid for this panel
51307      * @return {Roo.grid.Grid} 
51308      */
51309     getGrid : function(){
51310         return this.grid;    
51311     },
51312     
51313     setSize : function(width, height){
51314         if(!this.ignoreResize(width, height)){
51315             var grid = this.grid;
51316             var size = this.adjustForComponents(width, height);
51317             grid.getGridEl().setSize(size.width, size.height);
51318             grid.autoSize();
51319         }
51320     },
51321     
51322     beforeSlide : function(){
51323         this.grid.getView().scroller.clip();
51324     },
51325     
51326     afterSlide : function(){
51327         this.grid.getView().scroller.unclip();
51328     },
51329     
51330     destroy : function(){
51331         this.grid.destroy();
51332         delete this.grid;
51333         Roo.GridPanel.superclass.destroy.call(this); 
51334     }
51335 });
51336
51337
51338 /**
51339  * @class Roo.NestedLayoutPanel
51340  * @extends Roo.ContentPanel
51341  * @constructor
51342  * Create a new NestedLayoutPanel.
51343  * 
51344  * 
51345  * @param {Roo.BorderLayout} layout The layout for this panel
51346  * @param {String/Object} config A string to set only the title or a config object
51347  */
51348 Roo.NestedLayoutPanel = function(layout, config)
51349 {
51350     // construct with only one argument..
51351     /* FIXME - implement nicer consturctors
51352     if (layout.layout) {
51353         config = layout;
51354         layout = config.layout;
51355         delete config.layout;
51356     }
51357     if (layout.xtype && !layout.getEl) {
51358         // then layout needs constructing..
51359         layout = Roo.factory(layout, Roo);
51360     }
51361     */
51362     
51363     
51364     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51365     
51366     layout.monitorWindowResize = false; // turn off autosizing
51367     this.layout = layout;
51368     this.layout.getEl().addClass("x-layout-nested-layout");
51369     
51370     
51371     
51372     
51373 };
51374
51375 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51376
51377     setSize : function(width, height){
51378         if(!this.ignoreResize(width, height)){
51379             var size = this.adjustForComponents(width, height);
51380             var el = this.layout.getEl();
51381             el.setSize(size.width, size.height);
51382             var touch = el.dom.offsetWidth;
51383             this.layout.layout();
51384             // ie requires a double layout on the first pass
51385             if(Roo.isIE && !this.initialized){
51386                 this.initialized = true;
51387                 this.layout.layout();
51388             }
51389         }
51390     },
51391     
51392     // activate all subpanels if not currently active..
51393     
51394     setActiveState : function(active){
51395         this.active = active;
51396         if(!active){
51397             this.fireEvent("deactivate", this);
51398             return;
51399         }
51400         
51401         this.fireEvent("activate", this);
51402         // not sure if this should happen before or after..
51403         if (!this.layout) {
51404             return; // should not happen..
51405         }
51406         var reg = false;
51407         for (var r in this.layout.regions) {
51408             reg = this.layout.getRegion(r);
51409             if (reg.getActivePanel()) {
51410                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51411                 reg.setActivePanel(reg.getActivePanel());
51412                 continue;
51413             }
51414             if (!reg.panels.length) {
51415                 continue;
51416             }
51417             reg.showPanel(reg.getPanel(0));
51418         }
51419         
51420         
51421         
51422         
51423     },
51424     
51425     /**
51426      * Returns the nested BorderLayout for this panel
51427      * @return {Roo.BorderLayout} 
51428      */
51429     getLayout : function(){
51430         return this.layout;
51431     },
51432     
51433      /**
51434      * Adds a xtype elements to the layout of the nested panel
51435      * <pre><code>
51436
51437 panel.addxtype({
51438        xtype : 'ContentPanel',
51439        region: 'west',
51440        items: [ .... ]
51441    }
51442 );
51443
51444 panel.addxtype({
51445         xtype : 'NestedLayoutPanel',
51446         region: 'west',
51447         layout: {
51448            center: { },
51449            west: { }   
51450         },
51451         items : [ ... list of content panels or nested layout panels.. ]
51452    }
51453 );
51454 </code></pre>
51455      * @param {Object} cfg Xtype definition of item to add.
51456      */
51457     addxtype : function(cfg) {
51458         return this.layout.addxtype(cfg);
51459     
51460     }
51461 });
51462
51463 Roo.ScrollPanel = function(el, config, content){
51464     config = config || {};
51465     config.fitToFrame = true;
51466     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51467     
51468     this.el.dom.style.overflow = "hidden";
51469     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51470     this.el.removeClass("x-layout-inactive-content");
51471     this.el.on("mousewheel", this.onWheel, this);
51472
51473     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51474     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51475     up.unselectable(); down.unselectable();
51476     up.on("click", this.scrollUp, this);
51477     down.on("click", this.scrollDown, this);
51478     up.addClassOnOver("x-scroller-btn-over");
51479     down.addClassOnOver("x-scroller-btn-over");
51480     up.addClassOnClick("x-scroller-btn-click");
51481     down.addClassOnClick("x-scroller-btn-click");
51482     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51483
51484     this.resizeEl = this.el;
51485     this.el = wrap; this.up = up; this.down = down;
51486 };
51487
51488 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51489     increment : 100,
51490     wheelIncrement : 5,
51491     scrollUp : function(){
51492         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51493     },
51494
51495     scrollDown : function(){
51496         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51497     },
51498
51499     afterScroll : function(){
51500         var el = this.resizeEl;
51501         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51502         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51503         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51504     },
51505
51506     setSize : function(){
51507         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51508         this.afterScroll();
51509     },
51510
51511     onWheel : function(e){
51512         var d = e.getWheelDelta();
51513         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51514         this.afterScroll();
51515         e.stopEvent();
51516     },
51517
51518     setContent : function(content, loadScripts){
51519         this.resizeEl.update(content, loadScripts);
51520     }
51521
51522 });
51523
51524
51525
51526
51527
51528
51529
51530
51531
51532 /**
51533  * @class Roo.TreePanel
51534  * @extends Roo.ContentPanel
51535  * @constructor
51536  * Create a new TreePanel. - defaults to fit/scoll contents.
51537  * @param {String/Object} config A string to set only the panel's title, or a config object
51538  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51539  */
51540 Roo.TreePanel = function(config){
51541     var el = config.el;
51542     var tree = config.tree;
51543     delete config.tree; 
51544     delete config.el; // hopefull!
51545     
51546     // wrapper for IE7 strict & safari scroll issue
51547     
51548     var treeEl = el.createChild();
51549     config.resizeEl = treeEl;
51550     
51551     
51552     
51553     Roo.TreePanel.superclass.constructor.call(this, el, config);
51554  
51555  
51556     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51557     //console.log(tree);
51558     this.on('activate', function()
51559     {
51560         if (this.tree.rendered) {
51561             return;
51562         }
51563         //console.log('render tree');
51564         this.tree.render();
51565     });
51566     // this should not be needed.. - it's actually the 'el' that resizes?
51567     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51568     
51569     //this.on('resize',  function (cp, w, h) {
51570     //        this.tree.innerCt.setWidth(w);
51571     //        this.tree.innerCt.setHeight(h);
51572     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51573     //});
51574
51575         
51576     
51577 };
51578
51579 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51580     fitToFrame : true,
51581     autoScroll : true
51582 });
51583
51584
51585
51586
51587
51588
51589
51590
51591
51592
51593
51594 /*
51595  * Based on:
51596  * Ext JS Library 1.1.1
51597  * Copyright(c) 2006-2007, Ext JS, LLC.
51598  *
51599  * Originally Released Under LGPL - original licence link has changed is not relivant.
51600  *
51601  * Fork - LGPL
51602  * <script type="text/javascript">
51603  */
51604  
51605
51606 /**
51607  * @class Roo.ReaderLayout
51608  * @extends Roo.BorderLayout
51609  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51610  * center region containing two nested regions (a top one for a list view and one for item preview below),
51611  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51612  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51613  * expedites the setup of the overall layout and regions for this common application style.
51614  * Example:
51615  <pre><code>
51616 var reader = new Roo.ReaderLayout();
51617 var CP = Roo.ContentPanel;  // shortcut for adding
51618
51619 reader.beginUpdate();
51620 reader.add("north", new CP("north", "North"));
51621 reader.add("west", new CP("west", {title: "West"}));
51622 reader.add("east", new CP("east", {title: "East"}));
51623
51624 reader.regions.listView.add(new CP("listView", "List"));
51625 reader.regions.preview.add(new CP("preview", "Preview"));
51626 reader.endUpdate();
51627 </code></pre>
51628 * @constructor
51629 * Create a new ReaderLayout
51630 * @param {Object} config Configuration options
51631 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51632 * document.body if omitted)
51633 */
51634 Roo.ReaderLayout = function(config, renderTo){
51635     var c = config || {size:{}};
51636     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51637         north: c.north !== false ? Roo.apply({
51638             split:false,
51639             initialSize: 32,
51640             titlebar: false
51641         }, c.north) : false,
51642         west: c.west !== false ? Roo.apply({
51643             split:true,
51644             initialSize: 200,
51645             minSize: 175,
51646             maxSize: 400,
51647             titlebar: true,
51648             collapsible: true,
51649             animate: true,
51650             margins:{left:5,right:0,bottom:5,top:5},
51651             cmargins:{left:5,right:5,bottom:5,top:5}
51652         }, c.west) : false,
51653         east: c.east !== false ? Roo.apply({
51654             split:true,
51655             initialSize: 200,
51656             minSize: 175,
51657             maxSize: 400,
51658             titlebar: true,
51659             collapsible: true,
51660             animate: true,
51661             margins:{left:0,right:5,bottom:5,top:5},
51662             cmargins:{left:5,right:5,bottom:5,top:5}
51663         }, c.east) : false,
51664         center: Roo.apply({
51665             tabPosition: 'top',
51666             autoScroll:false,
51667             closeOnTab: true,
51668             titlebar:false,
51669             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51670         }, c.center)
51671     });
51672
51673     this.el.addClass('x-reader');
51674
51675     this.beginUpdate();
51676
51677     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51678         south: c.preview !== false ? Roo.apply({
51679             split:true,
51680             initialSize: 200,
51681             minSize: 100,
51682             autoScroll:true,
51683             collapsible:true,
51684             titlebar: true,
51685             cmargins:{top:5,left:0, right:0, bottom:0}
51686         }, c.preview) : false,
51687         center: Roo.apply({
51688             autoScroll:false,
51689             titlebar:false,
51690             minHeight:200
51691         }, c.listView)
51692     });
51693     this.add('center', new Roo.NestedLayoutPanel(inner,
51694             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51695
51696     this.endUpdate();
51697
51698     this.regions.preview = inner.getRegion('south');
51699     this.regions.listView = inner.getRegion('center');
51700 };
51701
51702 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51703  * Based on:
51704  * Ext JS Library 1.1.1
51705  * Copyright(c) 2006-2007, Ext JS, LLC.
51706  *
51707  * Originally Released Under LGPL - original licence link has changed is not relivant.
51708  *
51709  * Fork - LGPL
51710  * <script type="text/javascript">
51711  */
51712  
51713 /**
51714  * @class Roo.grid.Grid
51715  * @extends Roo.util.Observable
51716  * This class represents the primary interface of a component based grid control.
51717  * <br><br>Usage:<pre><code>
51718  var grid = new Roo.grid.Grid("my-container-id", {
51719      ds: myDataStore,
51720      cm: myColModel,
51721      selModel: mySelectionModel,
51722      autoSizeColumns: true,
51723      monitorWindowResize: false,
51724      trackMouseOver: true
51725  });
51726  // set any options
51727  grid.render();
51728  * </code></pre>
51729  * <b>Common Problems:</b><br/>
51730  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51731  * element will correct this<br/>
51732  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51733  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51734  * are unpredictable.<br/>
51735  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51736  * grid to calculate dimensions/offsets.<br/>
51737   * @constructor
51738  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51739  * The container MUST have some type of size defined for the grid to fill. The container will be
51740  * automatically set to position relative if it isn't already.
51741  * @param {Object} config A config object that sets properties on this grid.
51742  */
51743 Roo.grid.Grid = function(container, config){
51744         // initialize the container
51745         this.container = Roo.get(container);
51746         this.container.update("");
51747         this.container.setStyle("overflow", "hidden");
51748     this.container.addClass('x-grid-container');
51749
51750     this.id = this.container.id;
51751
51752     Roo.apply(this, config);
51753     // check and correct shorthanded configs
51754     if(this.ds){
51755         this.dataSource = this.ds;
51756         delete this.ds;
51757     }
51758     if(this.cm){
51759         this.colModel = this.cm;
51760         delete this.cm;
51761     }
51762     if(this.sm){
51763         this.selModel = this.sm;
51764         delete this.sm;
51765     }
51766
51767     if (this.selModel) {
51768         this.selModel = Roo.factory(this.selModel, Roo.grid);
51769         this.sm = this.selModel;
51770         this.sm.xmodule = this.xmodule || false;
51771     }
51772     if (typeof(this.colModel.config) == 'undefined') {
51773         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51774         this.cm = this.colModel;
51775         this.cm.xmodule = this.xmodule || false;
51776     }
51777     if (this.dataSource) {
51778         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51779         this.ds = this.dataSource;
51780         this.ds.xmodule = this.xmodule || false;
51781          
51782     }
51783     
51784     
51785     
51786     if(this.width){
51787         this.container.setWidth(this.width);
51788     }
51789
51790     if(this.height){
51791         this.container.setHeight(this.height);
51792     }
51793     /** @private */
51794         this.addEvents({
51795         // raw events
51796         /**
51797          * @event click
51798          * The raw click event for the entire grid.
51799          * @param {Roo.EventObject} e
51800          */
51801         "click" : true,
51802         /**
51803          * @event dblclick
51804          * The raw dblclick event for the entire grid.
51805          * @param {Roo.EventObject} e
51806          */
51807         "dblclick" : true,
51808         /**
51809          * @event contextmenu
51810          * The raw contextmenu event for the entire grid.
51811          * @param {Roo.EventObject} e
51812          */
51813         "contextmenu" : true,
51814         /**
51815          * @event mousedown
51816          * The raw mousedown event for the entire grid.
51817          * @param {Roo.EventObject} e
51818          */
51819         "mousedown" : true,
51820         /**
51821          * @event mouseup
51822          * The raw mouseup event for the entire grid.
51823          * @param {Roo.EventObject} e
51824          */
51825         "mouseup" : true,
51826         /**
51827          * @event mouseover
51828          * The raw mouseover event for the entire grid.
51829          * @param {Roo.EventObject} e
51830          */
51831         "mouseover" : true,
51832         /**
51833          * @event mouseout
51834          * The raw mouseout event for the entire grid.
51835          * @param {Roo.EventObject} e
51836          */
51837         "mouseout" : true,
51838         /**
51839          * @event keypress
51840          * The raw keypress event for the entire grid.
51841          * @param {Roo.EventObject} e
51842          */
51843         "keypress" : true,
51844         /**
51845          * @event keydown
51846          * The raw keydown event for the entire grid.
51847          * @param {Roo.EventObject} e
51848          */
51849         "keydown" : true,
51850
51851         // custom events
51852
51853         /**
51854          * @event cellclick
51855          * Fires when a cell is clicked
51856          * @param {Grid} this
51857          * @param {Number} rowIndex
51858          * @param {Number} columnIndex
51859          * @param {Roo.EventObject} e
51860          */
51861         "cellclick" : true,
51862         /**
51863          * @event celldblclick
51864          * Fires when a cell is double clicked
51865          * @param {Grid} this
51866          * @param {Number} rowIndex
51867          * @param {Number} columnIndex
51868          * @param {Roo.EventObject} e
51869          */
51870         "celldblclick" : true,
51871         /**
51872          * @event rowclick
51873          * Fires when a row is clicked
51874          * @param {Grid} this
51875          * @param {Number} rowIndex
51876          * @param {Roo.EventObject} e
51877          */
51878         "rowclick" : true,
51879         /**
51880          * @event rowdblclick
51881          * Fires when a row is double clicked
51882          * @param {Grid} this
51883          * @param {Number} rowIndex
51884          * @param {Roo.EventObject} e
51885          */
51886         "rowdblclick" : true,
51887         /**
51888          * @event headerclick
51889          * Fires when a header is clicked
51890          * @param {Grid} this
51891          * @param {Number} columnIndex
51892          * @param {Roo.EventObject} e
51893          */
51894         "headerclick" : true,
51895         /**
51896          * @event headerdblclick
51897          * Fires when a header cell is double clicked
51898          * @param {Grid} this
51899          * @param {Number} columnIndex
51900          * @param {Roo.EventObject} e
51901          */
51902         "headerdblclick" : true,
51903         /**
51904          * @event rowcontextmenu
51905          * Fires when a row is right clicked
51906          * @param {Grid} this
51907          * @param {Number} rowIndex
51908          * @param {Roo.EventObject} e
51909          */
51910         "rowcontextmenu" : true,
51911         /**
51912          * @event cellcontextmenu
51913          * Fires when a cell is right clicked
51914          * @param {Grid} this
51915          * @param {Number} rowIndex
51916          * @param {Number} cellIndex
51917          * @param {Roo.EventObject} e
51918          */
51919          "cellcontextmenu" : true,
51920         /**
51921          * @event headercontextmenu
51922          * Fires when a header is right clicked
51923          * @param {Grid} this
51924          * @param {Number} columnIndex
51925          * @param {Roo.EventObject} e
51926          */
51927         "headercontextmenu" : true,
51928         /**
51929          * @event bodyscroll
51930          * Fires when the body element is scrolled
51931          * @param {Number} scrollLeft
51932          * @param {Number} scrollTop
51933          */
51934         "bodyscroll" : true,
51935         /**
51936          * @event columnresize
51937          * Fires when the user resizes a column
51938          * @param {Number} columnIndex
51939          * @param {Number} newSize
51940          */
51941         "columnresize" : true,
51942         /**
51943          * @event columnmove
51944          * Fires when the user moves a column
51945          * @param {Number} oldIndex
51946          * @param {Number} newIndex
51947          */
51948         "columnmove" : true,
51949         /**
51950          * @event startdrag
51951          * Fires when row(s) start being dragged
51952          * @param {Grid} this
51953          * @param {Roo.GridDD} dd The drag drop object
51954          * @param {event} e The raw browser event
51955          */
51956         "startdrag" : true,
51957         /**
51958          * @event enddrag
51959          * Fires when a drag operation is complete
51960          * @param {Grid} this
51961          * @param {Roo.GridDD} dd The drag drop object
51962          * @param {event} e The raw browser event
51963          */
51964         "enddrag" : true,
51965         /**
51966          * @event dragdrop
51967          * Fires when dragged row(s) are dropped on a valid DD target
51968          * @param {Grid} this
51969          * @param {Roo.GridDD} dd The drag drop object
51970          * @param {String} targetId The target drag drop object
51971          * @param {event} e The raw browser event
51972          */
51973         "dragdrop" : true,
51974         /**
51975          * @event dragover
51976          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51977          * @param {Grid} this
51978          * @param {Roo.GridDD} dd The drag drop object
51979          * @param {String} targetId The target drag drop object
51980          * @param {event} e The raw browser event
51981          */
51982         "dragover" : true,
51983         /**
51984          * @event dragenter
51985          *  Fires when the dragged row(s) first cross another DD target while being dragged
51986          * @param {Grid} this
51987          * @param {Roo.GridDD} dd The drag drop object
51988          * @param {String} targetId The target drag drop object
51989          * @param {event} e The raw browser event
51990          */
51991         "dragenter" : true,
51992         /**
51993          * @event dragout
51994          * Fires when the dragged row(s) leave another DD target while being dragged
51995          * @param {Grid} this
51996          * @param {Roo.GridDD} dd The drag drop object
51997          * @param {String} targetId The target drag drop object
51998          * @param {event} e The raw browser event
51999          */
52000         "dragout" : true,
52001         /**
52002          * @event rowclass
52003          * Fires when a row is rendered, so you can change add a style to it.
52004          * @param {GridView} gridview   The grid view
52005          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52006          */
52007         'rowclass' : true,
52008
52009         /**
52010          * @event render
52011          * Fires when the grid is rendered
52012          * @param {Grid} grid
52013          */
52014         'render' : true
52015     });
52016
52017     Roo.grid.Grid.superclass.constructor.call(this);
52018 };
52019 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52020     
52021     /**
52022      * @cfg {String} ddGroup - drag drop group.
52023      */
52024
52025     /**
52026      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52027      */
52028     minColumnWidth : 25,
52029
52030     /**
52031      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52032      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52033      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52034      */
52035     autoSizeColumns : false,
52036
52037     /**
52038      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52039      */
52040     autoSizeHeaders : true,
52041
52042     /**
52043      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52044      */
52045     monitorWindowResize : true,
52046
52047     /**
52048      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52049      * rows measured to get a columns size. Default is 0 (all rows).
52050      */
52051     maxRowsToMeasure : 0,
52052
52053     /**
52054      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52055      */
52056     trackMouseOver : true,
52057
52058     /**
52059     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52060     */
52061     
52062     /**
52063     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52064     */
52065     enableDragDrop : false,
52066     
52067     /**
52068     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52069     */
52070     enableColumnMove : true,
52071     
52072     /**
52073     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52074     */
52075     enableColumnHide : true,
52076     
52077     /**
52078     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52079     */
52080     enableRowHeightSync : false,
52081     
52082     /**
52083     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52084     */
52085     stripeRows : true,
52086     
52087     /**
52088     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52089     */
52090     autoHeight : false,
52091
52092     /**
52093      * @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.
52094      */
52095     autoExpandColumn : false,
52096
52097     /**
52098     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52099     * Default is 50.
52100     */
52101     autoExpandMin : 50,
52102
52103     /**
52104     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52105     */
52106     autoExpandMax : 1000,
52107
52108     /**
52109     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52110     */
52111     view : null,
52112
52113     /**
52114     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52115     */
52116     loadMask : false,
52117     /**
52118     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52119     */
52120     dropTarget: false,
52121     
52122    
52123     
52124     // private
52125     rendered : false,
52126
52127     /**
52128     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52129     * of a fixed width. Default is false.
52130     */
52131     /**
52132     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52133     */
52134     /**
52135      * Called once after all setup has been completed and the grid is ready to be rendered.
52136      * @return {Roo.grid.Grid} this
52137      */
52138     render : function()
52139     {
52140         var c = this.container;
52141         // try to detect autoHeight/width mode
52142         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52143             this.autoHeight = true;
52144         }
52145         var view = this.getView();
52146         view.init(this);
52147
52148         c.on("click", this.onClick, this);
52149         c.on("dblclick", this.onDblClick, this);
52150         c.on("contextmenu", this.onContextMenu, this);
52151         c.on("keydown", this.onKeyDown, this);
52152         if (Roo.isTouch) {
52153             c.on("touchstart", this.onTouchStart, this);
52154         }
52155
52156         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52157
52158         this.getSelectionModel().init(this);
52159
52160         view.render();
52161
52162         if(this.loadMask){
52163             this.loadMask = new Roo.LoadMask(this.container,
52164                     Roo.apply({store:this.dataSource}, this.loadMask));
52165         }
52166         
52167         
52168         if (this.toolbar && this.toolbar.xtype) {
52169             this.toolbar.container = this.getView().getHeaderPanel(true);
52170             this.toolbar = new Roo.Toolbar(this.toolbar);
52171         }
52172         if (this.footer && this.footer.xtype) {
52173             this.footer.dataSource = this.getDataSource();
52174             this.footer.container = this.getView().getFooterPanel(true);
52175             this.footer = Roo.factory(this.footer, Roo);
52176         }
52177         if (this.dropTarget && this.dropTarget.xtype) {
52178             delete this.dropTarget.xtype;
52179             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52180         }
52181         
52182         
52183         this.rendered = true;
52184         this.fireEvent('render', this);
52185         return this;
52186     },
52187
52188         /**
52189          * Reconfigures the grid to use a different Store and Column Model.
52190          * The View will be bound to the new objects and refreshed.
52191          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52192          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52193          */
52194     reconfigure : function(dataSource, colModel){
52195         if(this.loadMask){
52196             this.loadMask.destroy();
52197             this.loadMask = new Roo.LoadMask(this.container,
52198                     Roo.apply({store:dataSource}, this.loadMask));
52199         }
52200         this.view.bind(dataSource, colModel);
52201         this.dataSource = dataSource;
52202         this.colModel = colModel;
52203         this.view.refresh(true);
52204     },
52205
52206     // private
52207     onKeyDown : function(e){
52208         this.fireEvent("keydown", e);
52209     },
52210
52211     /**
52212      * Destroy this grid.
52213      * @param {Boolean} removeEl True to remove the element
52214      */
52215     destroy : function(removeEl, keepListeners){
52216         if(this.loadMask){
52217             this.loadMask.destroy();
52218         }
52219         var c = this.container;
52220         c.removeAllListeners();
52221         this.view.destroy();
52222         this.colModel.purgeListeners();
52223         if(!keepListeners){
52224             this.purgeListeners();
52225         }
52226         c.update("");
52227         if(removeEl === true){
52228             c.remove();
52229         }
52230     },
52231
52232     // private
52233     processEvent : function(name, e){
52234         // does this fire select???
52235         Roo.log('grid:processEvent '  + name);
52236         
52237         if (name != 'touchstart' ) {
52238             this.fireEvent(name, e);    
52239         }
52240         
52241         var t = e.getTarget();
52242         var v = this.view;
52243         var header = v.findHeaderIndex(t);
52244         if(header !== false){
52245             var ename = name == 'touchstart' ? 'click' : name;
52246              
52247             this.fireEvent("header" + ename, this, header, e);
52248         }else{
52249             var row = v.findRowIndex(t);
52250             var cell = v.findCellIndex(t);
52251             if (name == 'touchstart') {
52252                 // first touch is always a click.
52253                 // hopefull this happens after selection is updated.?
52254                 name = false;
52255                 
52256                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52257                     var cs = this.selModel.getSelectedCell();
52258                     if (row == cs[0] && cell == cs[1]){
52259                         name = 'dblclick';
52260                     }
52261                 }
52262                 if (typeof(this.selModel.getSelections) != 'undefined') {
52263                     var cs = this.selModel.getSelections();
52264                     var ds = this.dataSource;
52265                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52266                         name = 'dblclick';
52267                     }
52268                 }
52269                 if (!name) {
52270                     return;
52271                 }
52272             }
52273             
52274             
52275             if(row !== false){
52276                 this.fireEvent("row" + name, this, row, e);
52277                 if(cell !== false){
52278                     this.fireEvent("cell" + name, this, row, cell, e);
52279                 }
52280             }
52281         }
52282     },
52283
52284     // private
52285     onClick : function(e){
52286         this.processEvent("click", e);
52287     },
52288    // private
52289     onTouchStart : function(e){
52290         this.processEvent("touchstart", e);
52291     },
52292
52293     // private
52294     onContextMenu : function(e, t){
52295         this.processEvent("contextmenu", e);
52296     },
52297
52298     // private
52299     onDblClick : function(e){
52300         this.processEvent("dblclick", e);
52301     },
52302
52303     // private
52304     walkCells : function(row, col, step, fn, scope){
52305         var cm = this.colModel, clen = cm.getColumnCount();
52306         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52307         if(step < 0){
52308             if(col < 0){
52309                 row--;
52310                 first = false;
52311             }
52312             while(row >= 0){
52313                 if(!first){
52314                     col = clen-1;
52315                 }
52316                 first = false;
52317                 while(col >= 0){
52318                     if(fn.call(scope || this, row, col, cm) === true){
52319                         return [row, col];
52320                     }
52321                     col--;
52322                 }
52323                 row--;
52324             }
52325         } else {
52326             if(col >= clen){
52327                 row++;
52328                 first = false;
52329             }
52330             while(row < rlen){
52331                 if(!first){
52332                     col = 0;
52333                 }
52334                 first = false;
52335                 while(col < clen){
52336                     if(fn.call(scope || this, row, col, cm) === true){
52337                         return [row, col];
52338                     }
52339                     col++;
52340                 }
52341                 row++;
52342             }
52343         }
52344         return null;
52345     },
52346
52347     // private
52348     getSelections : function(){
52349         return this.selModel.getSelections();
52350     },
52351
52352     /**
52353      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52354      * but if manual update is required this method will initiate it.
52355      */
52356     autoSize : function(){
52357         if(this.rendered){
52358             this.view.layout();
52359             if(this.view.adjustForScroll){
52360                 this.view.adjustForScroll();
52361             }
52362         }
52363     },
52364
52365     /**
52366      * Returns the grid's underlying element.
52367      * @return {Element} The element
52368      */
52369     getGridEl : function(){
52370         return this.container;
52371     },
52372
52373     // private for compatibility, overridden by editor grid
52374     stopEditing : function(){},
52375
52376     /**
52377      * Returns the grid's SelectionModel.
52378      * @return {SelectionModel}
52379      */
52380     getSelectionModel : function(){
52381         if(!this.selModel){
52382             this.selModel = new Roo.grid.RowSelectionModel();
52383         }
52384         return this.selModel;
52385     },
52386
52387     /**
52388      * Returns the grid's DataSource.
52389      * @return {DataSource}
52390      */
52391     getDataSource : function(){
52392         return this.dataSource;
52393     },
52394
52395     /**
52396      * Returns the grid's ColumnModel.
52397      * @return {ColumnModel}
52398      */
52399     getColumnModel : function(){
52400         return this.colModel;
52401     },
52402
52403     /**
52404      * Returns the grid's GridView object.
52405      * @return {GridView}
52406      */
52407     getView : function(){
52408         if(!this.view){
52409             this.view = new Roo.grid.GridView(this.viewConfig);
52410         }
52411         return this.view;
52412     },
52413     /**
52414      * Called to get grid's drag proxy text, by default returns this.ddText.
52415      * @return {String}
52416      */
52417     getDragDropText : function(){
52418         var count = this.selModel.getCount();
52419         return String.format(this.ddText, count, count == 1 ? '' : 's');
52420     }
52421 });
52422 /**
52423  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52424  * %0 is replaced with the number of selected rows.
52425  * @type String
52426  */
52427 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52428  * Based on:
52429  * Ext JS Library 1.1.1
52430  * Copyright(c) 2006-2007, Ext JS, LLC.
52431  *
52432  * Originally Released Under LGPL - original licence link has changed is not relivant.
52433  *
52434  * Fork - LGPL
52435  * <script type="text/javascript">
52436  */
52437  
52438 Roo.grid.AbstractGridView = function(){
52439         this.grid = null;
52440         
52441         this.events = {
52442             "beforerowremoved" : true,
52443             "beforerowsinserted" : true,
52444             "beforerefresh" : true,
52445             "rowremoved" : true,
52446             "rowsinserted" : true,
52447             "rowupdated" : true,
52448             "refresh" : true
52449         };
52450     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52451 };
52452
52453 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52454     rowClass : "x-grid-row",
52455     cellClass : "x-grid-cell",
52456     tdClass : "x-grid-td",
52457     hdClass : "x-grid-hd",
52458     splitClass : "x-grid-hd-split",
52459     
52460     init: function(grid){
52461         this.grid = grid;
52462                 var cid = this.grid.getGridEl().id;
52463         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52464         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52465         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52466         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52467         },
52468         
52469     getColumnRenderers : function(){
52470         var renderers = [];
52471         var cm = this.grid.colModel;
52472         var colCount = cm.getColumnCount();
52473         for(var i = 0; i < colCount; i++){
52474             renderers[i] = cm.getRenderer(i);
52475         }
52476         return renderers;
52477     },
52478     
52479     getColumnIds : function(){
52480         var ids = [];
52481         var cm = this.grid.colModel;
52482         var colCount = cm.getColumnCount();
52483         for(var i = 0; i < colCount; i++){
52484             ids[i] = cm.getColumnId(i);
52485         }
52486         return ids;
52487     },
52488     
52489     getDataIndexes : function(){
52490         if(!this.indexMap){
52491             this.indexMap = this.buildIndexMap();
52492         }
52493         return this.indexMap.colToData;
52494     },
52495     
52496     getColumnIndexByDataIndex : function(dataIndex){
52497         if(!this.indexMap){
52498             this.indexMap = this.buildIndexMap();
52499         }
52500         return this.indexMap.dataToCol[dataIndex];
52501     },
52502     
52503     /**
52504      * Set a css style for a column dynamically. 
52505      * @param {Number} colIndex The index of the column
52506      * @param {String} name The css property name
52507      * @param {String} value The css value
52508      */
52509     setCSSStyle : function(colIndex, name, value){
52510         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52511         Roo.util.CSS.updateRule(selector, name, value);
52512     },
52513     
52514     generateRules : function(cm){
52515         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52516         Roo.util.CSS.removeStyleSheet(rulesId);
52517         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52518             var cid = cm.getColumnId(i);
52519             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52520                          this.tdSelector, cid, " {\n}\n",
52521                          this.hdSelector, cid, " {\n}\n",
52522                          this.splitSelector, cid, " {\n}\n");
52523         }
52524         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52525     }
52526 });/*
52527  * Based on:
52528  * Ext JS Library 1.1.1
52529  * Copyright(c) 2006-2007, Ext JS, LLC.
52530  *
52531  * Originally Released Under LGPL - original licence link has changed is not relivant.
52532  *
52533  * Fork - LGPL
52534  * <script type="text/javascript">
52535  */
52536
52537 // private
52538 // This is a support class used internally by the Grid components
52539 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52540     this.grid = grid;
52541     this.view = grid.getView();
52542     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52543     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52544     if(hd2){
52545         this.setHandleElId(Roo.id(hd));
52546         this.setOuterHandleElId(Roo.id(hd2));
52547     }
52548     this.scroll = false;
52549 };
52550 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52551     maxDragWidth: 120,
52552     getDragData : function(e){
52553         var t = Roo.lib.Event.getTarget(e);
52554         var h = this.view.findHeaderCell(t);
52555         if(h){
52556             return {ddel: h.firstChild, header:h};
52557         }
52558         return false;
52559     },
52560
52561     onInitDrag : function(e){
52562         this.view.headersDisabled = true;
52563         var clone = this.dragData.ddel.cloneNode(true);
52564         clone.id = Roo.id();
52565         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52566         this.proxy.update(clone);
52567         return true;
52568     },
52569
52570     afterValidDrop : function(){
52571         var v = this.view;
52572         setTimeout(function(){
52573             v.headersDisabled = false;
52574         }, 50);
52575     },
52576
52577     afterInvalidDrop : function(){
52578         var v = this.view;
52579         setTimeout(function(){
52580             v.headersDisabled = false;
52581         }, 50);
52582     }
52583 });
52584 /*
52585  * Based on:
52586  * Ext JS Library 1.1.1
52587  * Copyright(c) 2006-2007, Ext JS, LLC.
52588  *
52589  * Originally Released Under LGPL - original licence link has changed is not relivant.
52590  *
52591  * Fork - LGPL
52592  * <script type="text/javascript">
52593  */
52594 // private
52595 // This is a support class used internally by the Grid components
52596 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52597     this.grid = grid;
52598     this.view = grid.getView();
52599     // split the proxies so they don't interfere with mouse events
52600     this.proxyTop = Roo.DomHelper.append(document.body, {
52601         cls:"col-move-top", html:"&#160;"
52602     }, true);
52603     this.proxyBottom = Roo.DomHelper.append(document.body, {
52604         cls:"col-move-bottom", html:"&#160;"
52605     }, true);
52606     this.proxyTop.hide = this.proxyBottom.hide = function(){
52607         this.setLeftTop(-100,-100);
52608         this.setStyle("visibility", "hidden");
52609     };
52610     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52611     // temporarily disabled
52612     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52613     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52614 };
52615 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52616     proxyOffsets : [-4, -9],
52617     fly: Roo.Element.fly,
52618
52619     getTargetFromEvent : function(e){
52620         var t = Roo.lib.Event.getTarget(e);
52621         var cindex = this.view.findCellIndex(t);
52622         if(cindex !== false){
52623             return this.view.getHeaderCell(cindex);
52624         }
52625         return null;
52626     },
52627
52628     nextVisible : function(h){
52629         var v = this.view, cm = this.grid.colModel;
52630         h = h.nextSibling;
52631         while(h){
52632             if(!cm.isHidden(v.getCellIndex(h))){
52633                 return h;
52634             }
52635             h = h.nextSibling;
52636         }
52637         return null;
52638     },
52639
52640     prevVisible : function(h){
52641         var v = this.view, cm = this.grid.colModel;
52642         h = h.prevSibling;
52643         while(h){
52644             if(!cm.isHidden(v.getCellIndex(h))){
52645                 return h;
52646             }
52647             h = h.prevSibling;
52648         }
52649         return null;
52650     },
52651
52652     positionIndicator : function(h, n, e){
52653         var x = Roo.lib.Event.getPageX(e);
52654         var r = Roo.lib.Dom.getRegion(n.firstChild);
52655         var px, pt, py = r.top + this.proxyOffsets[1];
52656         if((r.right - x) <= (r.right-r.left)/2){
52657             px = r.right+this.view.borderWidth;
52658             pt = "after";
52659         }else{
52660             px = r.left;
52661             pt = "before";
52662         }
52663         var oldIndex = this.view.getCellIndex(h);
52664         var newIndex = this.view.getCellIndex(n);
52665
52666         if(this.grid.colModel.isFixed(newIndex)){
52667             return false;
52668         }
52669
52670         var locked = this.grid.colModel.isLocked(newIndex);
52671
52672         if(pt == "after"){
52673             newIndex++;
52674         }
52675         if(oldIndex < newIndex){
52676             newIndex--;
52677         }
52678         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52679             return false;
52680         }
52681         px +=  this.proxyOffsets[0];
52682         this.proxyTop.setLeftTop(px, py);
52683         this.proxyTop.show();
52684         if(!this.bottomOffset){
52685             this.bottomOffset = this.view.mainHd.getHeight();
52686         }
52687         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52688         this.proxyBottom.show();
52689         return pt;
52690     },
52691
52692     onNodeEnter : function(n, dd, e, data){
52693         if(data.header != n){
52694             this.positionIndicator(data.header, n, e);
52695         }
52696     },
52697
52698     onNodeOver : function(n, dd, e, data){
52699         var result = false;
52700         if(data.header != n){
52701             result = this.positionIndicator(data.header, n, e);
52702         }
52703         if(!result){
52704             this.proxyTop.hide();
52705             this.proxyBottom.hide();
52706         }
52707         return result ? this.dropAllowed : this.dropNotAllowed;
52708     },
52709
52710     onNodeOut : function(n, dd, e, data){
52711         this.proxyTop.hide();
52712         this.proxyBottom.hide();
52713     },
52714
52715     onNodeDrop : function(n, dd, e, data){
52716         var h = data.header;
52717         if(h != n){
52718             var cm = this.grid.colModel;
52719             var x = Roo.lib.Event.getPageX(e);
52720             var r = Roo.lib.Dom.getRegion(n.firstChild);
52721             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52722             var oldIndex = this.view.getCellIndex(h);
52723             var newIndex = this.view.getCellIndex(n);
52724             var locked = cm.isLocked(newIndex);
52725             if(pt == "after"){
52726                 newIndex++;
52727             }
52728             if(oldIndex < newIndex){
52729                 newIndex--;
52730             }
52731             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52732                 return false;
52733             }
52734             cm.setLocked(oldIndex, locked, true);
52735             cm.moveColumn(oldIndex, newIndex);
52736             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52737             return true;
52738         }
52739         return false;
52740     }
52741 });
52742 /*
52743  * Based on:
52744  * Ext JS Library 1.1.1
52745  * Copyright(c) 2006-2007, Ext JS, LLC.
52746  *
52747  * Originally Released Under LGPL - original licence link has changed is not relivant.
52748  *
52749  * Fork - LGPL
52750  * <script type="text/javascript">
52751  */
52752   
52753 /**
52754  * @class Roo.grid.GridView
52755  * @extends Roo.util.Observable
52756  *
52757  * @constructor
52758  * @param {Object} config
52759  */
52760 Roo.grid.GridView = function(config){
52761     Roo.grid.GridView.superclass.constructor.call(this);
52762     this.el = null;
52763
52764     Roo.apply(this, config);
52765 };
52766
52767 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52768
52769     unselectable :  'unselectable="on"',
52770     unselectableCls :  'x-unselectable',
52771     
52772     
52773     rowClass : "x-grid-row",
52774
52775     cellClass : "x-grid-col",
52776
52777     tdClass : "x-grid-td",
52778
52779     hdClass : "x-grid-hd",
52780
52781     splitClass : "x-grid-split",
52782
52783     sortClasses : ["sort-asc", "sort-desc"],
52784
52785     enableMoveAnim : false,
52786
52787     hlColor: "C3DAF9",
52788
52789     dh : Roo.DomHelper,
52790
52791     fly : Roo.Element.fly,
52792
52793     css : Roo.util.CSS,
52794
52795     borderWidth: 1,
52796
52797     splitOffset: 3,
52798
52799     scrollIncrement : 22,
52800
52801     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52802
52803     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52804
52805     bind : function(ds, cm){
52806         if(this.ds){
52807             this.ds.un("load", this.onLoad, this);
52808             this.ds.un("datachanged", this.onDataChange, this);
52809             this.ds.un("add", this.onAdd, this);
52810             this.ds.un("remove", this.onRemove, this);
52811             this.ds.un("update", this.onUpdate, this);
52812             this.ds.un("clear", this.onClear, this);
52813         }
52814         if(ds){
52815             ds.on("load", this.onLoad, this);
52816             ds.on("datachanged", this.onDataChange, this);
52817             ds.on("add", this.onAdd, this);
52818             ds.on("remove", this.onRemove, this);
52819             ds.on("update", this.onUpdate, this);
52820             ds.on("clear", this.onClear, this);
52821         }
52822         this.ds = ds;
52823
52824         if(this.cm){
52825             this.cm.un("widthchange", this.onColWidthChange, this);
52826             this.cm.un("headerchange", this.onHeaderChange, this);
52827             this.cm.un("hiddenchange", this.onHiddenChange, this);
52828             this.cm.un("columnmoved", this.onColumnMove, this);
52829             this.cm.un("columnlockchange", this.onColumnLock, this);
52830         }
52831         if(cm){
52832             this.generateRules(cm);
52833             cm.on("widthchange", this.onColWidthChange, this);
52834             cm.on("headerchange", this.onHeaderChange, this);
52835             cm.on("hiddenchange", this.onHiddenChange, this);
52836             cm.on("columnmoved", this.onColumnMove, this);
52837             cm.on("columnlockchange", this.onColumnLock, this);
52838         }
52839         this.cm = cm;
52840     },
52841
52842     init: function(grid){
52843         Roo.grid.GridView.superclass.init.call(this, grid);
52844
52845         this.bind(grid.dataSource, grid.colModel);
52846
52847         grid.on("headerclick", this.handleHeaderClick, this);
52848
52849         if(grid.trackMouseOver){
52850             grid.on("mouseover", this.onRowOver, this);
52851             grid.on("mouseout", this.onRowOut, this);
52852         }
52853         grid.cancelTextSelection = function(){};
52854         this.gridId = grid.id;
52855
52856         var tpls = this.templates || {};
52857
52858         if(!tpls.master){
52859             tpls.master = new Roo.Template(
52860                '<div class="x-grid" hidefocus="true">',
52861                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52862                   '<div class="x-grid-topbar"></div>',
52863                   '<div class="x-grid-scroller"><div></div></div>',
52864                   '<div class="x-grid-locked">',
52865                       '<div class="x-grid-header">{lockedHeader}</div>',
52866                       '<div class="x-grid-body">{lockedBody}</div>',
52867                   "</div>",
52868                   '<div class="x-grid-viewport">',
52869                       '<div class="x-grid-header">{header}</div>',
52870                       '<div class="x-grid-body">{body}</div>',
52871                   "</div>",
52872                   '<div class="x-grid-bottombar"></div>',
52873                  
52874                   '<div class="x-grid-resize-proxy">&#160;</div>',
52875                "</div>"
52876             );
52877             tpls.master.disableformats = true;
52878         }
52879
52880         if(!tpls.header){
52881             tpls.header = new Roo.Template(
52882                '<table border="0" cellspacing="0" cellpadding="0">',
52883                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52884                "</table>{splits}"
52885             );
52886             tpls.header.disableformats = true;
52887         }
52888         tpls.header.compile();
52889
52890         if(!tpls.hcell){
52891             tpls.hcell = new Roo.Template(
52892                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52893                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52894                 "</div></td>"
52895              );
52896              tpls.hcell.disableFormats = true;
52897         }
52898         tpls.hcell.compile();
52899
52900         if(!tpls.hsplit){
52901             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52902                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52903             tpls.hsplit.disableFormats = true;
52904         }
52905         tpls.hsplit.compile();
52906
52907         if(!tpls.body){
52908             tpls.body = new Roo.Template(
52909                '<table border="0" cellspacing="0" cellpadding="0">',
52910                "<tbody>{rows}</tbody>",
52911                "</table>"
52912             );
52913             tpls.body.disableFormats = true;
52914         }
52915         tpls.body.compile();
52916
52917         if(!tpls.row){
52918             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52919             tpls.row.disableFormats = true;
52920         }
52921         tpls.row.compile();
52922
52923         if(!tpls.cell){
52924             tpls.cell = new Roo.Template(
52925                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52926                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52927                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52928                 "</td>"
52929             );
52930             tpls.cell.disableFormats = true;
52931         }
52932         tpls.cell.compile();
52933
52934         this.templates = tpls;
52935     },
52936
52937     // remap these for backwards compat
52938     onColWidthChange : function(){
52939         this.updateColumns.apply(this, arguments);
52940     },
52941     onHeaderChange : function(){
52942         this.updateHeaders.apply(this, arguments);
52943     }, 
52944     onHiddenChange : function(){
52945         this.handleHiddenChange.apply(this, arguments);
52946     },
52947     onColumnMove : function(){
52948         this.handleColumnMove.apply(this, arguments);
52949     },
52950     onColumnLock : function(){
52951         this.handleLockChange.apply(this, arguments);
52952     },
52953
52954     onDataChange : function(){
52955         this.refresh();
52956         this.updateHeaderSortState();
52957     },
52958
52959     onClear : function(){
52960         this.refresh();
52961     },
52962
52963     onUpdate : function(ds, record){
52964         this.refreshRow(record);
52965     },
52966
52967     refreshRow : function(record){
52968         var ds = this.ds, index;
52969         if(typeof record == 'number'){
52970             index = record;
52971             record = ds.getAt(index);
52972         }else{
52973             index = ds.indexOf(record);
52974         }
52975         this.insertRows(ds, index, index, true);
52976         this.onRemove(ds, record, index+1, true);
52977         this.syncRowHeights(index, index);
52978         this.layout();
52979         this.fireEvent("rowupdated", this, index, record);
52980     },
52981
52982     onAdd : function(ds, records, index){
52983         this.insertRows(ds, index, index + (records.length-1));
52984     },
52985
52986     onRemove : function(ds, record, index, isUpdate){
52987         if(isUpdate !== true){
52988             this.fireEvent("beforerowremoved", this, index, record);
52989         }
52990         var bt = this.getBodyTable(), lt = this.getLockedTable();
52991         if(bt.rows[index]){
52992             bt.firstChild.removeChild(bt.rows[index]);
52993         }
52994         if(lt.rows[index]){
52995             lt.firstChild.removeChild(lt.rows[index]);
52996         }
52997         if(isUpdate !== true){
52998             this.stripeRows(index);
52999             this.syncRowHeights(index, index);
53000             this.layout();
53001             this.fireEvent("rowremoved", this, index, record);
53002         }
53003     },
53004
53005     onLoad : function(){
53006         this.scrollToTop();
53007     },
53008
53009     /**
53010      * Scrolls the grid to the top
53011      */
53012     scrollToTop : function(){
53013         if(this.scroller){
53014             this.scroller.dom.scrollTop = 0;
53015             this.syncScroll();
53016         }
53017     },
53018
53019     /**
53020      * Gets a panel in the header of the grid that can be used for toolbars etc.
53021      * After modifying the contents of this panel a call to grid.autoSize() may be
53022      * required to register any changes in size.
53023      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53024      * @return Roo.Element
53025      */
53026     getHeaderPanel : function(doShow){
53027         if(doShow){
53028             this.headerPanel.show();
53029         }
53030         return this.headerPanel;
53031     },
53032
53033     /**
53034      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53035      * After modifying the contents of this panel a call to grid.autoSize() may be
53036      * required to register any changes in size.
53037      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53038      * @return Roo.Element
53039      */
53040     getFooterPanel : function(doShow){
53041         if(doShow){
53042             this.footerPanel.show();
53043         }
53044         return this.footerPanel;
53045     },
53046
53047     initElements : function(){
53048         var E = Roo.Element;
53049         var el = this.grid.getGridEl().dom.firstChild;
53050         var cs = el.childNodes;
53051
53052         this.el = new E(el);
53053         
53054          this.focusEl = new E(el.firstChild);
53055         this.focusEl.swallowEvent("click", true);
53056         
53057         this.headerPanel = new E(cs[1]);
53058         this.headerPanel.enableDisplayMode("block");
53059
53060         this.scroller = new E(cs[2]);
53061         this.scrollSizer = new E(this.scroller.dom.firstChild);
53062
53063         this.lockedWrap = new E(cs[3]);
53064         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53065         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53066
53067         this.mainWrap = new E(cs[4]);
53068         this.mainHd = new E(this.mainWrap.dom.firstChild);
53069         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53070
53071         this.footerPanel = new E(cs[5]);
53072         this.footerPanel.enableDisplayMode("block");
53073
53074         this.resizeProxy = new E(cs[6]);
53075
53076         this.headerSelector = String.format(
53077            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53078            this.lockedHd.id, this.mainHd.id
53079         );
53080
53081         this.splitterSelector = String.format(
53082            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53083            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53084         );
53085     },
53086     idToCssName : function(s)
53087     {
53088         return s.replace(/[^a-z0-9]+/ig, '-');
53089     },
53090
53091     getHeaderCell : function(index){
53092         return Roo.DomQuery.select(this.headerSelector)[index];
53093     },
53094
53095     getHeaderCellMeasure : function(index){
53096         return this.getHeaderCell(index).firstChild;
53097     },
53098
53099     getHeaderCellText : function(index){
53100         return this.getHeaderCell(index).firstChild.firstChild;
53101     },
53102
53103     getLockedTable : function(){
53104         return this.lockedBody.dom.firstChild;
53105     },
53106
53107     getBodyTable : function(){
53108         return this.mainBody.dom.firstChild;
53109     },
53110
53111     getLockedRow : function(index){
53112         return this.getLockedTable().rows[index];
53113     },
53114
53115     getRow : function(index){
53116         return this.getBodyTable().rows[index];
53117     },
53118
53119     getRowComposite : function(index){
53120         if(!this.rowEl){
53121             this.rowEl = new Roo.CompositeElementLite();
53122         }
53123         var els = [], lrow, mrow;
53124         if(lrow = this.getLockedRow(index)){
53125             els.push(lrow);
53126         }
53127         if(mrow = this.getRow(index)){
53128             els.push(mrow);
53129         }
53130         this.rowEl.elements = els;
53131         return this.rowEl;
53132     },
53133     /**
53134      * Gets the 'td' of the cell
53135      * 
53136      * @param {Integer} rowIndex row to select
53137      * @param {Integer} colIndex column to select
53138      * 
53139      * @return {Object} 
53140      */
53141     getCell : function(rowIndex, colIndex){
53142         var locked = this.cm.getLockedCount();
53143         var source;
53144         if(colIndex < locked){
53145             source = this.lockedBody.dom.firstChild;
53146         }else{
53147             source = this.mainBody.dom.firstChild;
53148             colIndex -= locked;
53149         }
53150         return source.rows[rowIndex].childNodes[colIndex];
53151     },
53152
53153     getCellText : function(rowIndex, colIndex){
53154         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53155     },
53156
53157     getCellBox : function(cell){
53158         var b = this.fly(cell).getBox();
53159         if(Roo.isOpera){ // opera fails to report the Y
53160             b.y = cell.offsetTop + this.mainBody.getY();
53161         }
53162         return b;
53163     },
53164
53165     getCellIndex : function(cell){
53166         var id = String(cell.className).match(this.cellRE);
53167         if(id){
53168             return parseInt(id[1], 10);
53169         }
53170         return 0;
53171     },
53172
53173     findHeaderIndex : function(n){
53174         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53175         return r ? this.getCellIndex(r) : false;
53176     },
53177
53178     findHeaderCell : function(n){
53179         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53180         return r ? r : false;
53181     },
53182
53183     findRowIndex : function(n){
53184         if(!n){
53185             return false;
53186         }
53187         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53188         return r ? r.rowIndex : false;
53189     },
53190
53191     findCellIndex : function(node){
53192         var stop = this.el.dom;
53193         while(node && node != stop){
53194             if(this.findRE.test(node.className)){
53195                 return this.getCellIndex(node);
53196             }
53197             node = node.parentNode;
53198         }
53199         return false;
53200     },
53201
53202     getColumnId : function(index){
53203         return this.cm.getColumnId(index);
53204     },
53205
53206     getSplitters : function()
53207     {
53208         if(this.splitterSelector){
53209            return Roo.DomQuery.select(this.splitterSelector);
53210         }else{
53211             return null;
53212       }
53213     },
53214
53215     getSplitter : function(index){
53216         return this.getSplitters()[index];
53217     },
53218
53219     onRowOver : function(e, t){
53220         var row;
53221         if((row = this.findRowIndex(t)) !== false){
53222             this.getRowComposite(row).addClass("x-grid-row-over");
53223         }
53224     },
53225
53226     onRowOut : function(e, t){
53227         var row;
53228         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53229             this.getRowComposite(row).removeClass("x-grid-row-over");
53230         }
53231     },
53232
53233     renderHeaders : function(){
53234         var cm = this.cm;
53235         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53236         var cb = [], lb = [], sb = [], lsb = [], p = {};
53237         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53238             p.cellId = "x-grid-hd-0-" + i;
53239             p.splitId = "x-grid-csplit-0-" + i;
53240             p.id = cm.getColumnId(i);
53241             p.title = cm.getColumnTooltip(i) || "";
53242             p.value = cm.getColumnHeader(i) || "";
53243             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53244             if(!cm.isLocked(i)){
53245                 cb[cb.length] = ct.apply(p);
53246                 sb[sb.length] = st.apply(p);
53247             }else{
53248                 lb[lb.length] = ct.apply(p);
53249                 lsb[lsb.length] = st.apply(p);
53250             }
53251         }
53252         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53253                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53254     },
53255
53256     updateHeaders : function(){
53257         var html = this.renderHeaders();
53258         this.lockedHd.update(html[0]);
53259         this.mainHd.update(html[1]);
53260     },
53261
53262     /**
53263      * Focuses the specified row.
53264      * @param {Number} row The row index
53265      */
53266     focusRow : function(row)
53267     {
53268         //Roo.log('GridView.focusRow');
53269         var x = this.scroller.dom.scrollLeft;
53270         this.focusCell(row, 0, false);
53271         this.scroller.dom.scrollLeft = x;
53272     },
53273
53274     /**
53275      * Focuses the specified cell.
53276      * @param {Number} row The row index
53277      * @param {Number} col The column index
53278      * @param {Boolean} hscroll false to disable horizontal scrolling
53279      */
53280     focusCell : function(row, col, hscroll)
53281     {
53282         //Roo.log('GridView.focusCell');
53283         var el = this.ensureVisible(row, col, hscroll);
53284         this.focusEl.alignTo(el, "tl-tl");
53285         if(Roo.isGecko){
53286             this.focusEl.focus();
53287         }else{
53288             this.focusEl.focus.defer(1, this.focusEl);
53289         }
53290     },
53291
53292     /**
53293      * Scrolls the specified cell into view
53294      * @param {Number} row The row index
53295      * @param {Number} col The column index
53296      * @param {Boolean} hscroll false to disable horizontal scrolling
53297      */
53298     ensureVisible : function(row, col, hscroll)
53299     {
53300         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53301         //return null; //disable for testing.
53302         if(typeof row != "number"){
53303             row = row.rowIndex;
53304         }
53305         if(row < 0 && row >= this.ds.getCount()){
53306             return  null;
53307         }
53308         col = (col !== undefined ? col : 0);
53309         var cm = this.grid.colModel;
53310         while(cm.isHidden(col)){
53311             col++;
53312         }
53313
53314         var el = this.getCell(row, col);
53315         if(!el){
53316             return null;
53317         }
53318         var c = this.scroller.dom;
53319
53320         var ctop = parseInt(el.offsetTop, 10);
53321         var cleft = parseInt(el.offsetLeft, 10);
53322         var cbot = ctop + el.offsetHeight;
53323         var cright = cleft + el.offsetWidth;
53324         
53325         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53326         var stop = parseInt(c.scrollTop, 10);
53327         var sleft = parseInt(c.scrollLeft, 10);
53328         var sbot = stop + ch;
53329         var sright = sleft + c.clientWidth;
53330         /*
53331         Roo.log('GridView.ensureVisible:' +
53332                 ' ctop:' + ctop +
53333                 ' c.clientHeight:' + c.clientHeight +
53334                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53335                 ' stop:' + stop +
53336                 ' cbot:' + cbot +
53337                 ' sbot:' + sbot +
53338                 ' ch:' + ch  
53339                 );
53340         */
53341         if(ctop < stop){
53342              c.scrollTop = ctop;
53343             //Roo.log("set scrolltop to ctop DISABLE?");
53344         }else if(cbot > sbot){
53345             //Roo.log("set scrolltop to cbot-ch");
53346             c.scrollTop = cbot-ch;
53347         }
53348         
53349         if(hscroll !== false){
53350             if(cleft < sleft){
53351                 c.scrollLeft = cleft;
53352             }else if(cright > sright){
53353                 c.scrollLeft = cright-c.clientWidth;
53354             }
53355         }
53356          
53357         return el;
53358     },
53359
53360     updateColumns : function(){
53361         this.grid.stopEditing();
53362         var cm = this.grid.colModel, colIds = this.getColumnIds();
53363         //var totalWidth = cm.getTotalWidth();
53364         var pos = 0;
53365         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53366             //if(cm.isHidden(i)) continue;
53367             var w = cm.getColumnWidth(i);
53368             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53369             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53370         }
53371         this.updateSplitters();
53372     },
53373
53374     generateRules : function(cm){
53375         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53376         Roo.util.CSS.removeStyleSheet(rulesId);
53377         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53378             var cid = cm.getColumnId(i);
53379             var align = '';
53380             if(cm.config[i].align){
53381                 align = 'text-align:'+cm.config[i].align+';';
53382             }
53383             var hidden = '';
53384             if(cm.isHidden(i)){
53385                 hidden = 'display:none;';
53386             }
53387             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53388             ruleBuf.push(
53389                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53390                     this.hdSelector, cid, " {\n", align, width, "}\n",
53391                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53392                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53393         }
53394         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53395     },
53396
53397     updateSplitters : function(){
53398         var cm = this.cm, s = this.getSplitters();
53399         if(s){ // splitters not created yet
53400             var pos = 0, locked = true;
53401             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53402                 if(cm.isHidden(i)) continue;
53403                 var w = cm.getColumnWidth(i); // make sure it's a number
53404                 if(!cm.isLocked(i) && locked){
53405                     pos = 0;
53406                     locked = false;
53407                 }
53408                 pos += w;
53409                 s[i].style.left = (pos-this.splitOffset) + "px";
53410             }
53411         }
53412     },
53413
53414     handleHiddenChange : function(colModel, colIndex, hidden){
53415         if(hidden){
53416             this.hideColumn(colIndex);
53417         }else{
53418             this.unhideColumn(colIndex);
53419         }
53420     },
53421
53422     hideColumn : function(colIndex){
53423         var cid = this.getColumnId(colIndex);
53424         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53425         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53426         if(Roo.isSafari){
53427             this.updateHeaders();
53428         }
53429         this.updateSplitters();
53430         this.layout();
53431     },
53432
53433     unhideColumn : function(colIndex){
53434         var cid = this.getColumnId(colIndex);
53435         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53436         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53437
53438         if(Roo.isSafari){
53439             this.updateHeaders();
53440         }
53441         this.updateSplitters();
53442         this.layout();
53443     },
53444
53445     insertRows : function(dm, firstRow, lastRow, isUpdate){
53446         if(firstRow == 0 && lastRow == dm.getCount()-1){
53447             this.refresh();
53448         }else{
53449             if(!isUpdate){
53450                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53451             }
53452             var s = this.getScrollState();
53453             var markup = this.renderRows(firstRow, lastRow);
53454             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53455             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53456             this.restoreScroll(s);
53457             if(!isUpdate){
53458                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53459                 this.syncRowHeights(firstRow, lastRow);
53460                 this.stripeRows(firstRow);
53461                 this.layout();
53462             }
53463         }
53464     },
53465
53466     bufferRows : function(markup, target, index){
53467         var before = null, trows = target.rows, tbody = target.tBodies[0];
53468         if(index < trows.length){
53469             before = trows[index];
53470         }
53471         var b = document.createElement("div");
53472         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53473         var rows = b.firstChild.rows;
53474         for(var i = 0, len = rows.length; i < len; i++){
53475             if(before){
53476                 tbody.insertBefore(rows[0], before);
53477             }else{
53478                 tbody.appendChild(rows[0]);
53479             }
53480         }
53481         b.innerHTML = "";
53482         b = null;
53483     },
53484
53485     deleteRows : function(dm, firstRow, lastRow){
53486         if(dm.getRowCount()<1){
53487             this.fireEvent("beforerefresh", this);
53488             this.mainBody.update("");
53489             this.lockedBody.update("");
53490             this.fireEvent("refresh", this);
53491         }else{
53492             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53493             var bt = this.getBodyTable();
53494             var tbody = bt.firstChild;
53495             var rows = bt.rows;
53496             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53497                 tbody.removeChild(rows[firstRow]);
53498             }
53499             this.stripeRows(firstRow);
53500             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53501         }
53502     },
53503
53504     updateRows : function(dataSource, firstRow, lastRow){
53505         var s = this.getScrollState();
53506         this.refresh();
53507         this.restoreScroll(s);
53508     },
53509
53510     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53511         if(!noRefresh){
53512            this.refresh();
53513         }
53514         this.updateHeaderSortState();
53515     },
53516
53517     getScrollState : function(){
53518         
53519         var sb = this.scroller.dom;
53520         return {left: sb.scrollLeft, top: sb.scrollTop};
53521     },
53522
53523     stripeRows : function(startRow){
53524         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53525             return;
53526         }
53527         startRow = startRow || 0;
53528         var rows = this.getBodyTable().rows;
53529         var lrows = this.getLockedTable().rows;
53530         var cls = ' x-grid-row-alt ';
53531         for(var i = startRow, len = rows.length; i < len; i++){
53532             var row = rows[i], lrow = lrows[i];
53533             var isAlt = ((i+1) % 2 == 0);
53534             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53535             if(isAlt == hasAlt){
53536                 continue;
53537             }
53538             if(isAlt){
53539                 row.className += " x-grid-row-alt";
53540             }else{
53541                 row.className = row.className.replace("x-grid-row-alt", "");
53542             }
53543             if(lrow){
53544                 lrow.className = row.className;
53545             }
53546         }
53547     },
53548
53549     restoreScroll : function(state){
53550         //Roo.log('GridView.restoreScroll');
53551         var sb = this.scroller.dom;
53552         sb.scrollLeft = state.left;
53553         sb.scrollTop = state.top;
53554         this.syncScroll();
53555     },
53556
53557     syncScroll : function(){
53558         //Roo.log('GridView.syncScroll');
53559         var sb = this.scroller.dom;
53560         var sh = this.mainHd.dom;
53561         var bs = this.mainBody.dom;
53562         var lv = this.lockedBody.dom;
53563         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53564         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53565     },
53566
53567     handleScroll : function(e){
53568         this.syncScroll();
53569         var sb = this.scroller.dom;
53570         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53571         e.stopEvent();
53572     },
53573
53574     handleWheel : function(e){
53575         var d = e.getWheelDelta();
53576         this.scroller.dom.scrollTop -= d*22;
53577         // set this here to prevent jumpy scrolling on large tables
53578         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53579         e.stopEvent();
53580     },
53581
53582     renderRows : function(startRow, endRow){
53583         // pull in all the crap needed to render rows
53584         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53585         var colCount = cm.getColumnCount();
53586
53587         if(ds.getCount() < 1){
53588             return ["", ""];
53589         }
53590
53591         // build a map for all the columns
53592         var cs = [];
53593         for(var i = 0; i < colCount; i++){
53594             var name = cm.getDataIndex(i);
53595             cs[i] = {
53596                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53597                 renderer : cm.getRenderer(i),
53598                 id : cm.getColumnId(i),
53599                 locked : cm.isLocked(i)
53600             };
53601         }
53602
53603         startRow = startRow || 0;
53604         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53605
53606         // records to render
53607         var rs = ds.getRange(startRow, endRow);
53608
53609         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53610     },
53611
53612     // As much as I hate to duplicate code, this was branched because FireFox really hates
53613     // [].join("") on strings. The performance difference was substantial enough to
53614     // branch this function
53615     doRender : Roo.isGecko ?
53616             function(cs, rs, ds, startRow, colCount, stripe){
53617                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53618                 // buffers
53619                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53620                 
53621                 var hasListener = this.grid.hasListener('rowclass');
53622                 var rowcfg = {};
53623                 for(var j = 0, len = rs.length; j < len; j++){
53624                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53625                     for(var i = 0; i < colCount; i++){
53626                         c = cs[i];
53627                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53628                         p.id = c.id;
53629                         p.css = p.attr = "";
53630                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53631                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53632                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53633                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53634                         }
53635                         var markup = ct.apply(p);
53636                         if(!c.locked){
53637                             cb+= markup;
53638                         }else{
53639                             lcb+= markup;
53640                         }
53641                     }
53642                     var alt = [];
53643                     if(stripe && ((rowIndex+1) % 2 == 0)){
53644                         alt.push("x-grid-row-alt")
53645                     }
53646                     if(r.dirty){
53647                         alt.push(  " x-grid-dirty-row");
53648                     }
53649                     rp.cells = lcb;
53650                     if(this.getRowClass){
53651                         alt.push(this.getRowClass(r, rowIndex));
53652                     }
53653                     if (hasListener) {
53654                         rowcfg = {
53655                              
53656                             record: r,
53657                             rowIndex : rowIndex,
53658                             rowClass : ''
53659                         }
53660                         this.grid.fireEvent('rowclass', this, rowcfg);
53661                         alt.push(rowcfg.rowClass);
53662                     }
53663                     rp.alt = alt.join(" ");
53664                     lbuf+= rt.apply(rp);
53665                     rp.cells = cb;
53666                     buf+=  rt.apply(rp);
53667                 }
53668                 return [lbuf, buf];
53669             } :
53670             function(cs, rs, ds, startRow, colCount, stripe){
53671                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53672                 // buffers
53673                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53674                 var hasListener = this.grid.hasListener('rowclass');
53675  
53676                 var rowcfg = {};
53677                 for(var j = 0, len = rs.length; j < len; j++){
53678                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53679                     for(var i = 0; i < colCount; i++){
53680                         c = cs[i];
53681                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53682                         p.id = c.id;
53683                         p.css = p.attr = "";
53684                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53685                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53686                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53687                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53688                         }
53689                         
53690                         var markup = ct.apply(p);
53691                         if(!c.locked){
53692                             cb[cb.length] = markup;
53693                         }else{
53694                             lcb[lcb.length] = markup;
53695                         }
53696                     }
53697                     var alt = [];
53698                     if(stripe && ((rowIndex+1) % 2 == 0)){
53699                         alt.push( "x-grid-row-alt");
53700                     }
53701                     if(r.dirty){
53702                         alt.push(" x-grid-dirty-row");
53703                     }
53704                     rp.cells = lcb;
53705                     if(this.getRowClass){
53706                         alt.push( this.getRowClass(r, rowIndex));
53707                     }
53708                     if (hasListener) {
53709                         rowcfg = {
53710                              
53711                             record: r,
53712                             rowIndex : rowIndex,
53713                             rowClass : ''
53714                         }
53715                         this.grid.fireEvent('rowclass', this, rowcfg);
53716                         alt.push(rowcfg.rowClass);
53717                     }
53718                     rp.alt = alt.join(" ");
53719                     rp.cells = lcb.join("");
53720                     lbuf[lbuf.length] = rt.apply(rp);
53721                     rp.cells = cb.join("");
53722                     buf[buf.length] =  rt.apply(rp);
53723                 }
53724                 return [lbuf.join(""), buf.join("")];
53725             },
53726
53727     renderBody : function(){
53728         var markup = this.renderRows();
53729         var bt = this.templates.body;
53730         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53731     },
53732
53733     /**
53734      * Refreshes the grid
53735      * @param {Boolean} headersToo
53736      */
53737     refresh : function(headersToo){
53738         this.fireEvent("beforerefresh", this);
53739         this.grid.stopEditing();
53740         var result = this.renderBody();
53741         this.lockedBody.update(result[0]);
53742         this.mainBody.update(result[1]);
53743         if(headersToo === true){
53744             this.updateHeaders();
53745             this.updateColumns();
53746             this.updateSplitters();
53747             this.updateHeaderSortState();
53748         }
53749         this.syncRowHeights();
53750         this.layout();
53751         this.fireEvent("refresh", this);
53752     },
53753
53754     handleColumnMove : function(cm, oldIndex, newIndex){
53755         this.indexMap = null;
53756         var s = this.getScrollState();
53757         this.refresh(true);
53758         this.restoreScroll(s);
53759         this.afterMove(newIndex);
53760     },
53761
53762     afterMove : function(colIndex){
53763         if(this.enableMoveAnim && Roo.enableFx){
53764             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53765         }
53766         // if multisort - fix sortOrder, and reload..
53767         if (this.grid.dataSource.multiSort) {
53768             // the we can call sort again..
53769             var dm = this.grid.dataSource;
53770             var cm = this.grid.colModel;
53771             var so = [];
53772             for(var i = 0; i < cm.config.length; i++ ) {
53773                 
53774                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53775                     continue; // dont' bother, it's not in sort list or being set.
53776                 }
53777                 
53778                 so.push(cm.config[i].dataIndex);
53779             };
53780             dm.sortOrder = so;
53781             dm.load(dm.lastOptions);
53782             
53783             
53784         }
53785         
53786     },
53787
53788     updateCell : function(dm, rowIndex, dataIndex){
53789         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53790         if(typeof colIndex == "undefined"){ // not present in grid
53791             return;
53792         }
53793         var cm = this.grid.colModel;
53794         var cell = this.getCell(rowIndex, colIndex);
53795         var cellText = this.getCellText(rowIndex, colIndex);
53796
53797         var p = {
53798             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53799             id : cm.getColumnId(colIndex),
53800             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53801         };
53802         var renderer = cm.getRenderer(colIndex);
53803         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53804         if(typeof val == "undefined" || val === "") val = "&#160;";
53805         cellText.innerHTML = val;
53806         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53807         this.syncRowHeights(rowIndex, rowIndex);
53808     },
53809
53810     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53811         var maxWidth = 0;
53812         if(this.grid.autoSizeHeaders){
53813             var h = this.getHeaderCellMeasure(colIndex);
53814             maxWidth = Math.max(maxWidth, h.scrollWidth);
53815         }
53816         var tb, index;
53817         if(this.cm.isLocked(colIndex)){
53818             tb = this.getLockedTable();
53819             index = colIndex;
53820         }else{
53821             tb = this.getBodyTable();
53822             index = colIndex - this.cm.getLockedCount();
53823         }
53824         if(tb && tb.rows){
53825             var rows = tb.rows;
53826             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53827             for(var i = 0; i < stopIndex; i++){
53828                 var cell = rows[i].childNodes[index].firstChild;
53829                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53830             }
53831         }
53832         return maxWidth + /*margin for error in IE*/ 5;
53833     },
53834     /**
53835      * Autofit a column to its content.
53836      * @param {Number} colIndex
53837      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53838      */
53839      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53840          if(this.cm.isHidden(colIndex)){
53841              return; // can't calc a hidden column
53842          }
53843         if(forceMinSize){
53844             var cid = this.cm.getColumnId(colIndex);
53845             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53846            if(this.grid.autoSizeHeaders){
53847                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53848            }
53849         }
53850         var newWidth = this.calcColumnWidth(colIndex);
53851         this.cm.setColumnWidth(colIndex,
53852             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53853         if(!suppressEvent){
53854             this.grid.fireEvent("columnresize", colIndex, newWidth);
53855         }
53856     },
53857
53858     /**
53859      * Autofits all columns to their content and then expands to fit any extra space in the grid
53860      */
53861      autoSizeColumns : function(){
53862         var cm = this.grid.colModel;
53863         var colCount = cm.getColumnCount();
53864         for(var i = 0; i < colCount; i++){
53865             this.autoSizeColumn(i, true, true);
53866         }
53867         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53868             this.fitColumns();
53869         }else{
53870             this.updateColumns();
53871             this.layout();
53872         }
53873     },
53874
53875     /**
53876      * Autofits all columns to the grid's width proportionate with their current size
53877      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53878      */
53879     fitColumns : function(reserveScrollSpace){
53880         var cm = this.grid.colModel;
53881         var colCount = cm.getColumnCount();
53882         var cols = [];
53883         var width = 0;
53884         var i, w;
53885         for (i = 0; i < colCount; i++){
53886             if(!cm.isHidden(i) && !cm.isFixed(i)){
53887                 w = cm.getColumnWidth(i);
53888                 cols.push(i);
53889                 cols.push(w);
53890                 width += w;
53891             }
53892         }
53893         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53894         if(reserveScrollSpace){
53895             avail -= 17;
53896         }
53897         var frac = (avail - cm.getTotalWidth())/width;
53898         while (cols.length){
53899             w = cols.pop();
53900             i = cols.pop();
53901             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53902         }
53903         this.updateColumns();
53904         this.layout();
53905     },
53906
53907     onRowSelect : function(rowIndex){
53908         var row = this.getRowComposite(rowIndex);
53909         row.addClass("x-grid-row-selected");
53910     },
53911
53912     onRowDeselect : function(rowIndex){
53913         var row = this.getRowComposite(rowIndex);
53914         row.removeClass("x-grid-row-selected");
53915     },
53916
53917     onCellSelect : function(row, col){
53918         var cell = this.getCell(row, col);
53919         if(cell){
53920             Roo.fly(cell).addClass("x-grid-cell-selected");
53921         }
53922     },
53923
53924     onCellDeselect : function(row, col){
53925         var cell = this.getCell(row, col);
53926         if(cell){
53927             Roo.fly(cell).removeClass("x-grid-cell-selected");
53928         }
53929     },
53930
53931     updateHeaderSortState : function(){
53932         
53933         // sort state can be single { field: xxx, direction : yyy}
53934         // or   { xxx=>ASC , yyy : DESC ..... }
53935         
53936         var mstate = {};
53937         if (!this.ds.multiSort) { 
53938             var state = this.ds.getSortState();
53939             if(!state){
53940                 return;
53941             }
53942             mstate[state.field] = state.direction;
53943             // FIXME... - this is not used here.. but might be elsewhere..
53944             this.sortState = state;
53945             
53946         } else {
53947             mstate = this.ds.sortToggle;
53948         }
53949         //remove existing sort classes..
53950         
53951         var sc = this.sortClasses;
53952         var hds = this.el.select(this.headerSelector).removeClass(sc);
53953         
53954         for(var f in mstate) {
53955         
53956             var sortColumn = this.cm.findColumnIndex(f);
53957             
53958             if(sortColumn != -1){
53959                 var sortDir = mstate[f];        
53960                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53961             }
53962         }
53963         
53964          
53965         
53966     },
53967
53968
53969     handleHeaderClick : function(g, index,e){
53970         
53971         Roo.log("header click");
53972         
53973         if (Roo.isTouch) {
53974             // touch events on header are handled by context
53975             this.handleHdCtx(g,index,e);
53976             return;
53977         }
53978         
53979         
53980         if(this.headersDisabled){
53981             return;
53982         }
53983         var dm = g.dataSource, cm = g.colModel;
53984         if(!cm.isSortable(index)){
53985             return;
53986         }
53987         g.stopEditing();
53988         
53989         if (dm.multiSort) {
53990             // update the sortOrder
53991             var so = [];
53992             for(var i = 0; i < cm.config.length; i++ ) {
53993                 
53994                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53995                     continue; // dont' bother, it's not in sort list or being set.
53996                 }
53997                 
53998                 so.push(cm.config[i].dataIndex);
53999             };
54000             dm.sortOrder = so;
54001         }
54002         
54003         
54004         dm.sort(cm.getDataIndex(index));
54005     },
54006
54007
54008     destroy : function(){
54009         if(this.colMenu){
54010             this.colMenu.removeAll();
54011             Roo.menu.MenuMgr.unregister(this.colMenu);
54012             this.colMenu.getEl().remove();
54013             delete this.colMenu;
54014         }
54015         if(this.hmenu){
54016             this.hmenu.removeAll();
54017             Roo.menu.MenuMgr.unregister(this.hmenu);
54018             this.hmenu.getEl().remove();
54019             delete this.hmenu;
54020         }
54021         if(this.grid.enableColumnMove){
54022             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54023             if(dds){
54024                 for(var dd in dds){
54025                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54026                         var elid = dds[dd].dragElId;
54027                         dds[dd].unreg();
54028                         Roo.get(elid).remove();
54029                     } else if(dds[dd].config.isTarget){
54030                         dds[dd].proxyTop.remove();
54031                         dds[dd].proxyBottom.remove();
54032                         dds[dd].unreg();
54033                     }
54034                     if(Roo.dd.DDM.locationCache[dd]){
54035                         delete Roo.dd.DDM.locationCache[dd];
54036                     }
54037                 }
54038                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54039             }
54040         }
54041         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54042         this.bind(null, null);
54043         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54044     },
54045
54046     handleLockChange : function(){
54047         this.refresh(true);
54048     },
54049
54050     onDenyColumnLock : function(){
54051
54052     },
54053
54054     onDenyColumnHide : function(){
54055
54056     },
54057
54058     handleHdMenuClick : function(item){
54059         var index = this.hdCtxIndex;
54060         var cm = this.cm, ds = this.ds;
54061         switch(item.id){
54062             case "asc":
54063                 ds.sort(cm.getDataIndex(index), "ASC");
54064                 break;
54065             case "desc":
54066                 ds.sort(cm.getDataIndex(index), "DESC");
54067                 break;
54068             case "lock":
54069                 var lc = cm.getLockedCount();
54070                 if(cm.getColumnCount(true) <= lc+1){
54071                     this.onDenyColumnLock();
54072                     return;
54073                 }
54074                 if(lc != index){
54075                     cm.setLocked(index, true, true);
54076                     cm.moveColumn(index, lc);
54077                     this.grid.fireEvent("columnmove", index, lc);
54078                 }else{
54079                     cm.setLocked(index, true);
54080                 }
54081             break;
54082             case "unlock":
54083                 var lc = cm.getLockedCount();
54084                 if((lc-1) != index){
54085                     cm.setLocked(index, false, true);
54086                     cm.moveColumn(index, lc-1);
54087                     this.grid.fireEvent("columnmove", index, lc-1);
54088                 }else{
54089                     cm.setLocked(index, false);
54090                 }
54091             break;
54092             case 'wider': // used to expand cols on touch..
54093             case 'narrow':
54094                 var cw = cm.getColumnWidth(index);
54095                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54096                 cw = Math.max(0, cw);
54097                 cw = Math.min(cw,4000);
54098                 cm.setColumnWidth(index, cw);
54099                 break;
54100                 
54101             default:
54102                 index = cm.getIndexById(item.id.substr(4));
54103                 if(index != -1){
54104                     if(item.checked && cm.getColumnCount(true) <= 1){
54105                         this.onDenyColumnHide();
54106                         return false;
54107                     }
54108                     cm.setHidden(index, item.checked);
54109                 }
54110         }
54111         return true;
54112     },
54113
54114     beforeColMenuShow : function(){
54115         var cm = this.cm,  colCount = cm.getColumnCount();
54116         this.colMenu.removeAll();
54117         for(var i = 0; i < colCount; i++){
54118             this.colMenu.add(new Roo.menu.CheckItem({
54119                 id: "col-"+cm.getColumnId(i),
54120                 text: cm.getColumnHeader(i),
54121                 checked: !cm.isHidden(i),
54122                 hideOnClick:false
54123             }));
54124         }
54125     },
54126
54127     handleHdCtx : function(g, index, e){
54128         e.stopEvent();
54129         var hd = this.getHeaderCell(index);
54130         this.hdCtxIndex = index;
54131         var ms = this.hmenu.items, cm = this.cm;
54132         ms.get("asc").setDisabled(!cm.isSortable(index));
54133         ms.get("desc").setDisabled(!cm.isSortable(index));
54134         if(this.grid.enableColLock !== false){
54135             ms.get("lock").setDisabled(cm.isLocked(index));
54136             ms.get("unlock").setDisabled(!cm.isLocked(index));
54137         }
54138         this.hmenu.show(hd, "tl-bl");
54139     },
54140
54141     handleHdOver : function(e){
54142         var hd = this.findHeaderCell(e.getTarget());
54143         if(hd && !this.headersDisabled){
54144             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54145                this.fly(hd).addClass("x-grid-hd-over");
54146             }
54147         }
54148     },
54149
54150     handleHdOut : function(e){
54151         var hd = this.findHeaderCell(e.getTarget());
54152         if(hd){
54153             this.fly(hd).removeClass("x-grid-hd-over");
54154         }
54155     },
54156
54157     handleSplitDblClick : function(e, t){
54158         var i = this.getCellIndex(t);
54159         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54160             this.autoSizeColumn(i, true);
54161             this.layout();
54162         }
54163     },
54164
54165     render : function(){
54166
54167         var cm = this.cm;
54168         var colCount = cm.getColumnCount();
54169
54170         if(this.grid.monitorWindowResize === true){
54171             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54172         }
54173         var header = this.renderHeaders();
54174         var body = this.templates.body.apply({rows:""});
54175         var html = this.templates.master.apply({
54176             lockedBody: body,
54177             body: body,
54178             lockedHeader: header[0],
54179             header: header[1]
54180         });
54181
54182         //this.updateColumns();
54183
54184         this.grid.getGridEl().dom.innerHTML = html;
54185
54186         this.initElements();
54187         
54188         // a kludge to fix the random scolling effect in webkit
54189         this.el.on("scroll", function() {
54190             this.el.dom.scrollTop=0; // hopefully not recursive..
54191         },this);
54192
54193         this.scroller.on("scroll", this.handleScroll, this);
54194         this.lockedBody.on("mousewheel", this.handleWheel, this);
54195         this.mainBody.on("mousewheel", this.handleWheel, this);
54196
54197         this.mainHd.on("mouseover", this.handleHdOver, this);
54198         this.mainHd.on("mouseout", this.handleHdOut, this);
54199         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54200                 {delegate: "."+this.splitClass});
54201
54202         this.lockedHd.on("mouseover", this.handleHdOver, this);
54203         this.lockedHd.on("mouseout", this.handleHdOut, this);
54204         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54205                 {delegate: "."+this.splitClass});
54206
54207         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54208             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54209         }
54210
54211         this.updateSplitters();
54212
54213         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54214             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54215             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54216         }
54217
54218         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54219             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54220             this.hmenu.add(
54221                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54222                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54223             );
54224             if(this.grid.enableColLock !== false){
54225                 this.hmenu.add('-',
54226                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54227                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54228                 );
54229             }
54230             if (Roo.isTouch) {
54231                  this.hmenu.add('-',
54232                     {id:"wider", text: this.columnsWiderText},
54233                     {id:"narrow", text: this.columnsNarrowText }
54234                 );
54235                 
54236                  
54237             }
54238             
54239             if(this.grid.enableColumnHide !== false){
54240
54241                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54242                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54243                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54244
54245                 this.hmenu.add('-',
54246                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54247                 );
54248             }
54249             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54250
54251             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54252         }
54253
54254         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54255             this.dd = new Roo.grid.GridDragZone(this.grid, {
54256                 ddGroup : this.grid.ddGroup || 'GridDD'
54257             });
54258             
54259         }
54260
54261         /*
54262         for(var i = 0; i < colCount; i++){
54263             if(cm.isHidden(i)){
54264                 this.hideColumn(i);
54265             }
54266             if(cm.config[i].align){
54267                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54268                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54269             }
54270         }*/
54271         
54272         this.updateHeaderSortState();
54273
54274         this.beforeInitialResize();
54275         this.layout(true);
54276
54277         // two part rendering gives faster view to the user
54278         this.renderPhase2.defer(1, this);
54279     },
54280
54281     renderPhase2 : function(){
54282         // render the rows now
54283         this.refresh();
54284         if(this.grid.autoSizeColumns){
54285             this.autoSizeColumns();
54286         }
54287     },
54288
54289     beforeInitialResize : function(){
54290
54291     },
54292
54293     onColumnSplitterMoved : function(i, w){
54294         this.userResized = true;
54295         var cm = this.grid.colModel;
54296         cm.setColumnWidth(i, w, true);
54297         var cid = cm.getColumnId(i);
54298         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54299         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54300         this.updateSplitters();
54301         this.layout();
54302         this.grid.fireEvent("columnresize", i, w);
54303     },
54304
54305     syncRowHeights : function(startIndex, endIndex){
54306         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54307             startIndex = startIndex || 0;
54308             var mrows = this.getBodyTable().rows;
54309             var lrows = this.getLockedTable().rows;
54310             var len = mrows.length-1;
54311             endIndex = Math.min(endIndex || len, len);
54312             for(var i = startIndex; i <= endIndex; i++){
54313                 var m = mrows[i], l = lrows[i];
54314                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54315                 m.style.height = l.style.height = h + "px";
54316             }
54317         }
54318     },
54319
54320     layout : function(initialRender, is2ndPass){
54321         var g = this.grid;
54322         var auto = g.autoHeight;
54323         var scrollOffset = 16;
54324         var c = g.getGridEl(), cm = this.cm,
54325                 expandCol = g.autoExpandColumn,
54326                 gv = this;
54327         //c.beginMeasure();
54328
54329         if(!c.dom.offsetWidth){ // display:none?
54330             if(initialRender){
54331                 this.lockedWrap.show();
54332                 this.mainWrap.show();
54333             }
54334             return;
54335         }
54336
54337         var hasLock = this.cm.isLocked(0);
54338
54339         var tbh = this.headerPanel.getHeight();
54340         var bbh = this.footerPanel.getHeight();
54341
54342         if(auto){
54343             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54344             var newHeight = ch + c.getBorderWidth("tb");
54345             if(g.maxHeight){
54346                 newHeight = Math.min(g.maxHeight, newHeight);
54347             }
54348             c.setHeight(newHeight);
54349         }
54350
54351         if(g.autoWidth){
54352             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54353         }
54354
54355         var s = this.scroller;
54356
54357         var csize = c.getSize(true);
54358
54359         this.el.setSize(csize.width, csize.height);
54360
54361         this.headerPanel.setWidth(csize.width);
54362         this.footerPanel.setWidth(csize.width);
54363
54364         var hdHeight = this.mainHd.getHeight();
54365         var vw = csize.width;
54366         var vh = csize.height - (tbh + bbh);
54367
54368         s.setSize(vw, vh);
54369
54370         var bt = this.getBodyTable();
54371         var ltWidth = hasLock ?
54372                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54373
54374         var scrollHeight = bt.offsetHeight;
54375         var scrollWidth = ltWidth + bt.offsetWidth;
54376         var vscroll = false, hscroll = false;
54377
54378         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54379
54380         var lw = this.lockedWrap, mw = this.mainWrap;
54381         var lb = this.lockedBody, mb = this.mainBody;
54382
54383         setTimeout(function(){
54384             var t = s.dom.offsetTop;
54385             var w = s.dom.clientWidth,
54386                 h = s.dom.clientHeight;
54387
54388             lw.setTop(t);
54389             lw.setSize(ltWidth, h);
54390
54391             mw.setLeftTop(ltWidth, t);
54392             mw.setSize(w-ltWidth, h);
54393
54394             lb.setHeight(h-hdHeight);
54395             mb.setHeight(h-hdHeight);
54396
54397             if(is2ndPass !== true && !gv.userResized && expandCol){
54398                 // high speed resize without full column calculation
54399                 
54400                 var ci = cm.getIndexById(expandCol);
54401                 if (ci < 0) {
54402                     ci = cm.findColumnIndex(expandCol);
54403                 }
54404                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54405                 var expandId = cm.getColumnId(ci);
54406                 var  tw = cm.getTotalWidth(false);
54407                 var currentWidth = cm.getColumnWidth(ci);
54408                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54409                 if(currentWidth != cw){
54410                     cm.setColumnWidth(ci, cw, true);
54411                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54412                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54413                     gv.updateSplitters();
54414                     gv.layout(false, true);
54415                 }
54416             }
54417
54418             if(initialRender){
54419                 lw.show();
54420                 mw.show();
54421             }
54422             //c.endMeasure();
54423         }, 10);
54424     },
54425
54426     onWindowResize : function(){
54427         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54428             return;
54429         }
54430         this.layout();
54431     },
54432
54433     appendFooter : function(parentEl){
54434         return null;
54435     },
54436
54437     sortAscText : "Sort Ascending",
54438     sortDescText : "Sort Descending",
54439     lockText : "Lock Column",
54440     unlockText : "Unlock Column",
54441     columnsText : "Columns",
54442  
54443     columnsWiderText : "Wider",
54444     columnsNarrowText : "Thinner"
54445 });
54446
54447
54448 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54449     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54450     this.proxy.el.addClass('x-grid3-col-dd');
54451 };
54452
54453 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54454     handleMouseDown : function(e){
54455
54456     },
54457
54458     callHandleMouseDown : function(e){
54459         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54460     }
54461 });
54462 /*
54463  * Based on:
54464  * Ext JS Library 1.1.1
54465  * Copyright(c) 2006-2007, Ext JS, LLC.
54466  *
54467  * Originally Released Under LGPL - original licence link has changed is not relivant.
54468  *
54469  * Fork - LGPL
54470  * <script type="text/javascript">
54471  */
54472  
54473 // private
54474 // This is a support class used internally by the Grid components
54475 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54476     this.grid = grid;
54477     this.view = grid.getView();
54478     this.proxy = this.view.resizeProxy;
54479     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54480         "gridSplitters" + this.grid.getGridEl().id, {
54481         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54482     });
54483     this.setHandleElId(Roo.id(hd));
54484     this.setOuterHandleElId(Roo.id(hd2));
54485     this.scroll = false;
54486 };
54487 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54488     fly: Roo.Element.fly,
54489
54490     b4StartDrag : function(x, y){
54491         this.view.headersDisabled = true;
54492         this.proxy.setHeight(this.view.mainWrap.getHeight());
54493         var w = this.cm.getColumnWidth(this.cellIndex);
54494         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54495         this.resetConstraints();
54496         this.setXConstraint(minw, 1000);
54497         this.setYConstraint(0, 0);
54498         this.minX = x - minw;
54499         this.maxX = x + 1000;
54500         this.startPos = x;
54501         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54502     },
54503
54504
54505     handleMouseDown : function(e){
54506         ev = Roo.EventObject.setEvent(e);
54507         var t = this.fly(ev.getTarget());
54508         if(t.hasClass("x-grid-split")){
54509             this.cellIndex = this.view.getCellIndex(t.dom);
54510             this.split = t.dom;
54511             this.cm = this.grid.colModel;
54512             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54513                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54514             }
54515         }
54516     },
54517
54518     endDrag : function(e){
54519         this.view.headersDisabled = false;
54520         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54521         var diff = endX - this.startPos;
54522         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54523     },
54524
54525     autoOffset : function(){
54526         this.setDelta(0,0);
54527     }
54528 });/*
54529  * Based on:
54530  * Ext JS Library 1.1.1
54531  * Copyright(c) 2006-2007, Ext JS, LLC.
54532  *
54533  * Originally Released Under LGPL - original licence link has changed is not relivant.
54534  *
54535  * Fork - LGPL
54536  * <script type="text/javascript">
54537  */
54538  
54539 // private
54540 // This is a support class used internally by the Grid components
54541 Roo.grid.GridDragZone = function(grid, config){
54542     this.view = grid.getView();
54543     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54544     if(this.view.lockedBody){
54545         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54546         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54547     }
54548     this.scroll = false;
54549     this.grid = grid;
54550     this.ddel = document.createElement('div');
54551     this.ddel.className = 'x-grid-dd-wrap';
54552 };
54553
54554 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54555     ddGroup : "GridDD",
54556
54557     getDragData : function(e){
54558         var t = Roo.lib.Event.getTarget(e);
54559         var rowIndex = this.view.findRowIndex(t);
54560         var sm = this.grid.selModel;
54561             
54562         //Roo.log(rowIndex);
54563         
54564         if (sm.getSelectedCell) {
54565             // cell selection..
54566             if (!sm.getSelectedCell()) {
54567                 return false;
54568             }
54569             if (rowIndex != sm.getSelectedCell()[0]) {
54570                 return false;
54571             }
54572         
54573         }
54574         
54575         if(rowIndex !== false){
54576             
54577             // if editorgrid.. 
54578             
54579             
54580             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54581                
54582             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54583               //  
54584             //}
54585             if (e.hasModifier()){
54586                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54587             }
54588             
54589             Roo.log("getDragData");
54590             
54591             return {
54592                 grid: this.grid,
54593                 ddel: this.ddel,
54594                 rowIndex: rowIndex,
54595                 selections:sm.getSelections ? sm.getSelections() : (
54596                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54597                 )
54598             };
54599         }
54600         return false;
54601     },
54602
54603     onInitDrag : function(e){
54604         var data = this.dragData;
54605         this.ddel.innerHTML = this.grid.getDragDropText();
54606         this.proxy.update(this.ddel);
54607         // fire start drag?
54608     },
54609
54610     afterRepair : function(){
54611         this.dragging = false;
54612     },
54613
54614     getRepairXY : function(e, data){
54615         return false;
54616     },
54617
54618     onEndDrag : function(data, e){
54619         // fire end drag?
54620     },
54621
54622     onValidDrop : function(dd, e, id){
54623         // fire drag drop?
54624         this.hideProxy();
54625     },
54626
54627     beforeInvalidDrop : function(e, id){
54628
54629     }
54630 });/*
54631  * Based on:
54632  * Ext JS Library 1.1.1
54633  * Copyright(c) 2006-2007, Ext JS, LLC.
54634  *
54635  * Originally Released Under LGPL - original licence link has changed is not relivant.
54636  *
54637  * Fork - LGPL
54638  * <script type="text/javascript">
54639  */
54640  
54641
54642 /**
54643  * @class Roo.grid.ColumnModel
54644  * @extends Roo.util.Observable
54645  * This is the default implementation of a ColumnModel used by the Grid. It defines
54646  * the columns in the grid.
54647  * <br>Usage:<br>
54648  <pre><code>
54649  var colModel = new Roo.grid.ColumnModel([
54650         {header: "Ticker", width: 60, sortable: true, locked: true},
54651         {header: "Company Name", width: 150, sortable: true},
54652         {header: "Market Cap.", width: 100, sortable: true},
54653         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54654         {header: "Employees", width: 100, sortable: true, resizable: false}
54655  ]);
54656  </code></pre>
54657  * <p>
54658  
54659  * The config options listed for this class are options which may appear in each
54660  * individual column definition.
54661  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54662  * @constructor
54663  * @param {Object} config An Array of column config objects. See this class's
54664  * config objects for details.
54665 */
54666 Roo.grid.ColumnModel = function(config){
54667         /**
54668      * The config passed into the constructor
54669      */
54670     this.config = config;
54671     this.lookup = {};
54672
54673     // if no id, create one
54674     // if the column does not have a dataIndex mapping,
54675     // map it to the order it is in the config
54676     for(var i = 0, len = config.length; i < len; i++){
54677         var c = config[i];
54678         if(typeof c.dataIndex == "undefined"){
54679             c.dataIndex = i;
54680         }
54681         if(typeof c.renderer == "string"){
54682             c.renderer = Roo.util.Format[c.renderer];
54683         }
54684         if(typeof c.id == "undefined"){
54685             c.id = Roo.id();
54686         }
54687         if(c.editor && c.editor.xtype){
54688             c.editor  = Roo.factory(c.editor, Roo.grid);
54689         }
54690         if(c.editor && c.editor.isFormField){
54691             c.editor = new Roo.grid.GridEditor(c.editor);
54692         }
54693         this.lookup[c.id] = c;
54694     }
54695
54696     /**
54697      * The width of columns which have no width specified (defaults to 100)
54698      * @type Number
54699      */
54700     this.defaultWidth = 100;
54701
54702     /**
54703      * Default sortable of columns which have no sortable specified (defaults to false)
54704      * @type Boolean
54705      */
54706     this.defaultSortable = false;
54707
54708     this.addEvents({
54709         /**
54710              * @event widthchange
54711              * Fires when the width of a column changes.
54712              * @param {ColumnModel} this
54713              * @param {Number} columnIndex The column index
54714              * @param {Number} newWidth The new width
54715              */
54716             "widthchange": true,
54717         /**
54718              * @event headerchange
54719              * Fires when the text of a header changes.
54720              * @param {ColumnModel} this
54721              * @param {Number} columnIndex The column index
54722              * @param {Number} newText The new header text
54723              */
54724             "headerchange": true,
54725         /**
54726              * @event hiddenchange
54727              * Fires when a column is hidden or "unhidden".
54728              * @param {ColumnModel} this
54729              * @param {Number} columnIndex The column index
54730              * @param {Boolean} hidden true if hidden, false otherwise
54731              */
54732             "hiddenchange": true,
54733             /**
54734          * @event columnmoved
54735          * Fires when a column is moved.
54736          * @param {ColumnModel} this
54737          * @param {Number} oldIndex
54738          * @param {Number} newIndex
54739          */
54740         "columnmoved" : true,
54741         /**
54742          * @event columlockchange
54743          * Fires when a column's locked state is changed
54744          * @param {ColumnModel} this
54745          * @param {Number} colIndex
54746          * @param {Boolean} locked true if locked
54747          */
54748         "columnlockchange" : true
54749     });
54750     Roo.grid.ColumnModel.superclass.constructor.call(this);
54751 };
54752 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54753     /**
54754      * @cfg {String} header The header text to display in the Grid view.
54755      */
54756     /**
54757      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54758      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54759      * specified, the column's index is used as an index into the Record's data Array.
54760      */
54761     /**
54762      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54763      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54764      */
54765     /**
54766      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54767      * Defaults to the value of the {@link #defaultSortable} property.
54768      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54769      */
54770     /**
54771      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54772      */
54773     /**
54774      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54775      */
54776     /**
54777      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54778      */
54779     /**
54780      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54781      */
54782     /**
54783      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54784      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54785      * default renderer uses the raw data value.
54786      */
54787        /**
54788      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54789      */
54790     /**
54791      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54792      */
54793
54794     /**
54795      * Returns the id of the column at the specified index.
54796      * @param {Number} index The column index
54797      * @return {String} the id
54798      */
54799     getColumnId : function(index){
54800         return this.config[index].id;
54801     },
54802
54803     /**
54804      * Returns the column for a specified id.
54805      * @param {String} id The column id
54806      * @return {Object} the column
54807      */
54808     getColumnById : function(id){
54809         return this.lookup[id];
54810     },
54811
54812     
54813     /**
54814      * Returns the column for a specified dataIndex.
54815      * @param {String} dataIndex The column dataIndex
54816      * @return {Object|Boolean} the column or false if not found
54817      */
54818     getColumnByDataIndex: function(dataIndex){
54819         var index = this.findColumnIndex(dataIndex);
54820         return index > -1 ? this.config[index] : false;
54821     },
54822     
54823     /**
54824      * Returns the index for a specified column id.
54825      * @param {String} id The column id
54826      * @return {Number} the index, or -1 if not found
54827      */
54828     getIndexById : function(id){
54829         for(var i = 0, len = this.config.length; i < len; i++){
54830             if(this.config[i].id == id){
54831                 return i;
54832             }
54833         }
54834         return -1;
54835     },
54836     
54837     /**
54838      * Returns the index for a specified column dataIndex.
54839      * @param {String} dataIndex The column dataIndex
54840      * @return {Number} the index, or -1 if not found
54841      */
54842     
54843     findColumnIndex : function(dataIndex){
54844         for(var i = 0, len = this.config.length; i < len; i++){
54845             if(this.config[i].dataIndex == dataIndex){
54846                 return i;
54847             }
54848         }
54849         return -1;
54850     },
54851     
54852     
54853     moveColumn : function(oldIndex, newIndex){
54854         var c = this.config[oldIndex];
54855         this.config.splice(oldIndex, 1);
54856         this.config.splice(newIndex, 0, c);
54857         this.dataMap = null;
54858         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54859     },
54860
54861     isLocked : function(colIndex){
54862         return this.config[colIndex].locked === true;
54863     },
54864
54865     setLocked : function(colIndex, value, suppressEvent){
54866         if(this.isLocked(colIndex) == value){
54867             return;
54868         }
54869         this.config[colIndex].locked = value;
54870         if(!suppressEvent){
54871             this.fireEvent("columnlockchange", this, colIndex, value);
54872         }
54873     },
54874
54875     getTotalLockedWidth : function(){
54876         var totalWidth = 0;
54877         for(var i = 0; i < this.config.length; i++){
54878             if(this.isLocked(i) && !this.isHidden(i)){
54879                 this.totalWidth += this.getColumnWidth(i);
54880             }
54881         }
54882         return totalWidth;
54883     },
54884
54885     getLockedCount : function(){
54886         for(var i = 0, len = this.config.length; i < len; i++){
54887             if(!this.isLocked(i)){
54888                 return i;
54889             }
54890         }
54891     },
54892
54893     /**
54894      * Returns the number of columns.
54895      * @return {Number}
54896      */
54897     getColumnCount : function(visibleOnly){
54898         if(visibleOnly === true){
54899             var c = 0;
54900             for(var i = 0, len = this.config.length; i < len; i++){
54901                 if(!this.isHidden(i)){
54902                     c++;
54903                 }
54904             }
54905             return c;
54906         }
54907         return this.config.length;
54908     },
54909
54910     /**
54911      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54912      * @param {Function} fn
54913      * @param {Object} scope (optional)
54914      * @return {Array} result
54915      */
54916     getColumnsBy : function(fn, scope){
54917         var r = [];
54918         for(var i = 0, len = this.config.length; i < len; i++){
54919             var c = this.config[i];
54920             if(fn.call(scope||this, c, i) === true){
54921                 r[r.length] = c;
54922             }
54923         }
54924         return r;
54925     },
54926
54927     /**
54928      * Returns true if the specified column is sortable.
54929      * @param {Number} col The column index
54930      * @return {Boolean}
54931      */
54932     isSortable : function(col){
54933         if(typeof this.config[col].sortable == "undefined"){
54934             return this.defaultSortable;
54935         }
54936         return this.config[col].sortable;
54937     },
54938
54939     /**
54940      * Returns the rendering (formatting) function defined for the column.
54941      * @param {Number} col The column index.
54942      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54943      */
54944     getRenderer : function(col){
54945         if(!this.config[col].renderer){
54946             return Roo.grid.ColumnModel.defaultRenderer;
54947         }
54948         return this.config[col].renderer;
54949     },
54950
54951     /**
54952      * Sets the rendering (formatting) function for a column.
54953      * @param {Number} col The column index
54954      * @param {Function} fn The function to use to process the cell's raw data
54955      * to return HTML markup for the grid view. The render function is called with
54956      * the following parameters:<ul>
54957      * <li>Data value.</li>
54958      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54959      * <li>css A CSS style string to apply to the table cell.</li>
54960      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54961      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54962      * <li>Row index</li>
54963      * <li>Column index</li>
54964      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54965      */
54966     setRenderer : function(col, fn){
54967         this.config[col].renderer = fn;
54968     },
54969
54970     /**
54971      * Returns the width for the specified column.
54972      * @param {Number} col The column index
54973      * @return {Number}
54974      */
54975     getColumnWidth : function(col){
54976         return this.config[col].width * 1 || this.defaultWidth;
54977     },
54978
54979     /**
54980      * Sets the width for a column.
54981      * @param {Number} col The column index
54982      * @param {Number} width The new width
54983      */
54984     setColumnWidth : function(col, width, suppressEvent){
54985         this.config[col].width = width;
54986         this.totalWidth = null;
54987         if(!suppressEvent){
54988              this.fireEvent("widthchange", this, col, width);
54989         }
54990     },
54991
54992     /**
54993      * Returns the total width of all columns.
54994      * @param {Boolean} includeHidden True to include hidden column widths
54995      * @return {Number}
54996      */
54997     getTotalWidth : function(includeHidden){
54998         if(!this.totalWidth){
54999             this.totalWidth = 0;
55000             for(var i = 0, len = this.config.length; i < len; i++){
55001                 if(includeHidden || !this.isHidden(i)){
55002                     this.totalWidth += this.getColumnWidth(i);
55003                 }
55004             }
55005         }
55006         return this.totalWidth;
55007     },
55008
55009     /**
55010      * Returns the header for the specified column.
55011      * @param {Number} col The column index
55012      * @return {String}
55013      */
55014     getColumnHeader : function(col){
55015         return this.config[col].header;
55016     },
55017
55018     /**
55019      * Sets the header for a column.
55020      * @param {Number} col The column index
55021      * @param {String} header The new header
55022      */
55023     setColumnHeader : function(col, header){
55024         this.config[col].header = header;
55025         this.fireEvent("headerchange", this, col, header);
55026     },
55027
55028     /**
55029      * Returns the tooltip for the specified column.
55030      * @param {Number} col The column index
55031      * @return {String}
55032      */
55033     getColumnTooltip : function(col){
55034             return this.config[col].tooltip;
55035     },
55036     /**
55037      * Sets the tooltip for a column.
55038      * @param {Number} col The column index
55039      * @param {String} tooltip The new tooltip
55040      */
55041     setColumnTooltip : function(col, tooltip){
55042             this.config[col].tooltip = tooltip;
55043     },
55044
55045     /**
55046      * Returns the dataIndex for the specified column.
55047      * @param {Number} col The column index
55048      * @return {Number}
55049      */
55050     getDataIndex : function(col){
55051         return this.config[col].dataIndex;
55052     },
55053
55054     /**
55055      * Sets the dataIndex for a column.
55056      * @param {Number} col The column index
55057      * @param {Number} dataIndex The new dataIndex
55058      */
55059     setDataIndex : function(col, dataIndex){
55060         this.config[col].dataIndex = dataIndex;
55061     },
55062
55063     
55064     
55065     /**
55066      * Returns true if the cell is editable.
55067      * @param {Number} colIndex The column index
55068      * @param {Number} rowIndex The row index
55069      * @return {Boolean}
55070      */
55071     isCellEditable : function(colIndex, rowIndex){
55072         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55073     },
55074
55075     /**
55076      * Returns the editor defined for the cell/column.
55077      * return false or null to disable editing.
55078      * @param {Number} colIndex The column index
55079      * @param {Number} rowIndex The row index
55080      * @return {Object}
55081      */
55082     getCellEditor : function(colIndex, rowIndex){
55083         return this.config[colIndex].editor;
55084     },
55085
55086     /**
55087      * Sets if a column is editable.
55088      * @param {Number} col The column index
55089      * @param {Boolean} editable True if the column is editable
55090      */
55091     setEditable : function(col, editable){
55092         this.config[col].editable = editable;
55093     },
55094
55095
55096     /**
55097      * Returns true if the column is hidden.
55098      * @param {Number} colIndex The column index
55099      * @return {Boolean}
55100      */
55101     isHidden : function(colIndex){
55102         return this.config[colIndex].hidden;
55103     },
55104
55105
55106     /**
55107      * Returns true if the column width cannot be changed
55108      */
55109     isFixed : function(colIndex){
55110         return this.config[colIndex].fixed;
55111     },
55112
55113     /**
55114      * Returns true if the column can be resized
55115      * @return {Boolean}
55116      */
55117     isResizable : function(colIndex){
55118         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55119     },
55120     /**
55121      * Sets if a column is hidden.
55122      * @param {Number} colIndex The column index
55123      * @param {Boolean} hidden True if the column is hidden
55124      */
55125     setHidden : function(colIndex, hidden){
55126         this.config[colIndex].hidden = hidden;
55127         this.totalWidth = null;
55128         this.fireEvent("hiddenchange", this, colIndex, hidden);
55129     },
55130
55131     /**
55132      * Sets the editor for a column.
55133      * @param {Number} col The column index
55134      * @param {Object} editor The editor object
55135      */
55136     setEditor : function(col, editor){
55137         this.config[col].editor = editor;
55138     }
55139 });
55140
55141 Roo.grid.ColumnModel.defaultRenderer = function(value){
55142         if(typeof value == "string" && value.length < 1){
55143             return "&#160;";
55144         }
55145         return value;
55146 };
55147
55148 // Alias for backwards compatibility
55149 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55150 /*
55151  * Based on:
55152  * Ext JS Library 1.1.1
55153  * Copyright(c) 2006-2007, Ext JS, LLC.
55154  *
55155  * Originally Released Under LGPL - original licence link has changed is not relivant.
55156  *
55157  * Fork - LGPL
55158  * <script type="text/javascript">
55159  */
55160
55161 /**
55162  * @class Roo.grid.AbstractSelectionModel
55163  * @extends Roo.util.Observable
55164  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55165  * implemented by descendant classes.  This class should not be directly instantiated.
55166  * @constructor
55167  */
55168 Roo.grid.AbstractSelectionModel = function(){
55169     this.locked = false;
55170     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55171 };
55172
55173 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55174     /** @ignore Called by the grid automatically. Do not call directly. */
55175     init : function(grid){
55176         this.grid = grid;
55177         this.initEvents();
55178     },
55179
55180     /**
55181      * Locks the selections.
55182      */
55183     lock : function(){
55184         this.locked = true;
55185     },
55186
55187     /**
55188      * Unlocks the selections.
55189      */
55190     unlock : function(){
55191         this.locked = false;
55192     },
55193
55194     /**
55195      * Returns true if the selections are locked.
55196      * @return {Boolean}
55197      */
55198     isLocked : function(){
55199         return this.locked;
55200     }
55201 });/*
55202  * Based on:
55203  * Ext JS Library 1.1.1
55204  * Copyright(c) 2006-2007, Ext JS, LLC.
55205  *
55206  * Originally Released Under LGPL - original licence link has changed is not relivant.
55207  *
55208  * Fork - LGPL
55209  * <script type="text/javascript">
55210  */
55211 /**
55212  * @extends Roo.grid.AbstractSelectionModel
55213  * @class Roo.grid.RowSelectionModel
55214  * The default SelectionModel used by {@link Roo.grid.Grid}.
55215  * It supports multiple selections and keyboard selection/navigation. 
55216  * @constructor
55217  * @param {Object} config
55218  */
55219 Roo.grid.RowSelectionModel = function(config){
55220     Roo.apply(this, config);
55221     this.selections = new Roo.util.MixedCollection(false, function(o){
55222         return o.id;
55223     });
55224
55225     this.last = false;
55226     this.lastActive = false;
55227
55228     this.addEvents({
55229         /**
55230              * @event selectionchange
55231              * Fires when the selection changes
55232              * @param {SelectionModel} this
55233              */
55234             "selectionchange" : true,
55235         /**
55236              * @event afterselectionchange
55237              * Fires after the selection changes (eg. by key press or clicking)
55238              * @param {SelectionModel} this
55239              */
55240             "afterselectionchange" : true,
55241         /**
55242              * @event beforerowselect
55243              * Fires when a row is selected being selected, return false to cancel.
55244              * @param {SelectionModel} this
55245              * @param {Number} rowIndex The selected index
55246              * @param {Boolean} keepExisting False if other selections will be cleared
55247              */
55248             "beforerowselect" : true,
55249         /**
55250              * @event rowselect
55251              * Fires when a row is selected.
55252              * @param {SelectionModel} this
55253              * @param {Number} rowIndex The selected index
55254              * @param {Roo.data.Record} r The record
55255              */
55256             "rowselect" : true,
55257         /**
55258              * @event rowdeselect
55259              * Fires when a row is deselected.
55260              * @param {SelectionModel} this
55261              * @param {Number} rowIndex The selected index
55262              */
55263         "rowdeselect" : true
55264     });
55265     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55266     this.locked = false;
55267 };
55268
55269 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55270     /**
55271      * @cfg {Boolean} singleSelect
55272      * True to allow selection of only one row at a time (defaults to false)
55273      */
55274     singleSelect : false,
55275
55276     // private
55277     initEvents : function(){
55278
55279         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55280             this.grid.on("mousedown", this.handleMouseDown, this);
55281         }else{ // allow click to work like normal
55282             this.grid.on("rowclick", this.handleDragableRowClick, this);
55283         }
55284
55285         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55286             "up" : function(e){
55287                 if(!e.shiftKey){
55288                     this.selectPrevious(e.shiftKey);
55289                 }else if(this.last !== false && this.lastActive !== false){
55290                     var last = this.last;
55291                     this.selectRange(this.last,  this.lastActive-1);
55292                     this.grid.getView().focusRow(this.lastActive);
55293                     if(last !== false){
55294                         this.last = last;
55295                     }
55296                 }else{
55297                     this.selectFirstRow();
55298                 }
55299                 this.fireEvent("afterselectionchange", this);
55300             },
55301             "down" : function(e){
55302                 if(!e.shiftKey){
55303                     this.selectNext(e.shiftKey);
55304                 }else if(this.last !== false && this.lastActive !== false){
55305                     var last = this.last;
55306                     this.selectRange(this.last,  this.lastActive+1);
55307                     this.grid.getView().focusRow(this.lastActive);
55308                     if(last !== false){
55309                         this.last = last;
55310                     }
55311                 }else{
55312                     this.selectFirstRow();
55313                 }
55314                 this.fireEvent("afterselectionchange", this);
55315             },
55316             scope: this
55317         });
55318
55319         var view = this.grid.view;
55320         view.on("refresh", this.onRefresh, this);
55321         view.on("rowupdated", this.onRowUpdated, this);
55322         view.on("rowremoved", this.onRemove, this);
55323     },
55324
55325     // private
55326     onRefresh : function(){
55327         var ds = this.grid.dataSource, i, v = this.grid.view;
55328         var s = this.selections;
55329         s.each(function(r){
55330             if((i = ds.indexOfId(r.id)) != -1){
55331                 v.onRowSelect(i);
55332             }else{
55333                 s.remove(r);
55334             }
55335         });
55336     },
55337
55338     // private
55339     onRemove : function(v, index, r){
55340         this.selections.remove(r);
55341     },
55342
55343     // private
55344     onRowUpdated : function(v, index, r){
55345         if(this.isSelected(r)){
55346             v.onRowSelect(index);
55347         }
55348     },
55349
55350     /**
55351      * Select records.
55352      * @param {Array} records The records to select
55353      * @param {Boolean} keepExisting (optional) True to keep existing selections
55354      */
55355     selectRecords : function(records, keepExisting){
55356         if(!keepExisting){
55357             this.clearSelections();
55358         }
55359         var ds = this.grid.dataSource;
55360         for(var i = 0, len = records.length; i < len; i++){
55361             this.selectRow(ds.indexOf(records[i]), true);
55362         }
55363     },
55364
55365     /**
55366      * Gets the number of selected rows.
55367      * @return {Number}
55368      */
55369     getCount : function(){
55370         return this.selections.length;
55371     },
55372
55373     /**
55374      * Selects the first row in the grid.
55375      */
55376     selectFirstRow : function(){
55377         this.selectRow(0);
55378     },
55379
55380     /**
55381      * Select the last row.
55382      * @param {Boolean} keepExisting (optional) True to keep existing selections
55383      */
55384     selectLastRow : function(keepExisting){
55385         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55386     },
55387
55388     /**
55389      * Selects the row immediately following the last selected row.
55390      * @param {Boolean} keepExisting (optional) True to keep existing selections
55391      */
55392     selectNext : function(keepExisting){
55393         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55394             this.selectRow(this.last+1, keepExisting);
55395             this.grid.getView().focusRow(this.last);
55396         }
55397     },
55398
55399     /**
55400      * Selects the row that precedes the last selected row.
55401      * @param {Boolean} keepExisting (optional) True to keep existing selections
55402      */
55403     selectPrevious : function(keepExisting){
55404         if(this.last){
55405             this.selectRow(this.last-1, keepExisting);
55406             this.grid.getView().focusRow(this.last);
55407         }
55408     },
55409
55410     /**
55411      * Returns the selected records
55412      * @return {Array} Array of selected records
55413      */
55414     getSelections : function(){
55415         return [].concat(this.selections.items);
55416     },
55417
55418     /**
55419      * Returns the first selected record.
55420      * @return {Record}
55421      */
55422     getSelected : function(){
55423         return this.selections.itemAt(0);
55424     },
55425
55426
55427     /**
55428      * Clears all selections.
55429      */
55430     clearSelections : function(fast){
55431         if(this.locked) return;
55432         if(fast !== true){
55433             var ds = this.grid.dataSource;
55434             var s = this.selections;
55435             s.each(function(r){
55436                 this.deselectRow(ds.indexOfId(r.id));
55437             }, this);
55438             s.clear();
55439         }else{
55440             this.selections.clear();
55441         }
55442         this.last = false;
55443     },
55444
55445
55446     /**
55447      * Selects all rows.
55448      */
55449     selectAll : function(){
55450         if(this.locked) return;
55451         this.selections.clear();
55452         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55453             this.selectRow(i, true);
55454         }
55455     },
55456
55457     /**
55458      * Returns True if there is a selection.
55459      * @return {Boolean}
55460      */
55461     hasSelection : function(){
55462         return this.selections.length > 0;
55463     },
55464
55465     /**
55466      * Returns True if the specified row is selected.
55467      * @param {Number/Record} record The record or index of the record to check
55468      * @return {Boolean}
55469      */
55470     isSelected : function(index){
55471         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55472         return (r && this.selections.key(r.id) ? true : false);
55473     },
55474
55475     /**
55476      * Returns True if the specified record id is selected.
55477      * @param {String} id The id of record to check
55478      * @return {Boolean}
55479      */
55480     isIdSelected : function(id){
55481         return (this.selections.key(id) ? true : false);
55482     },
55483
55484     // private
55485     handleMouseDown : function(e, t){
55486         var view = this.grid.getView(), rowIndex;
55487         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55488             return;
55489         };
55490         if(e.shiftKey && this.last !== false){
55491             var last = this.last;
55492             this.selectRange(last, rowIndex, e.ctrlKey);
55493             this.last = last; // reset the last
55494             view.focusRow(rowIndex);
55495         }else{
55496             var isSelected = this.isSelected(rowIndex);
55497             if(e.button !== 0 && isSelected){
55498                 view.focusRow(rowIndex);
55499             }else if(e.ctrlKey && isSelected){
55500                 this.deselectRow(rowIndex);
55501             }else if(!isSelected){
55502                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55503                 view.focusRow(rowIndex);
55504             }
55505         }
55506         this.fireEvent("afterselectionchange", this);
55507     },
55508     // private
55509     handleDragableRowClick :  function(grid, rowIndex, e) 
55510     {
55511         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55512             this.selectRow(rowIndex, false);
55513             grid.view.focusRow(rowIndex);
55514              this.fireEvent("afterselectionchange", this);
55515         }
55516     },
55517     
55518     /**
55519      * Selects multiple rows.
55520      * @param {Array} rows Array of the indexes of the row to select
55521      * @param {Boolean} keepExisting (optional) True to keep existing selections
55522      */
55523     selectRows : function(rows, keepExisting){
55524         if(!keepExisting){
55525             this.clearSelections();
55526         }
55527         for(var i = 0, len = rows.length; i < len; i++){
55528             this.selectRow(rows[i], true);
55529         }
55530     },
55531
55532     /**
55533      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55534      * @param {Number} startRow The index of the first row in the range
55535      * @param {Number} endRow The index of the last row in the range
55536      * @param {Boolean} keepExisting (optional) True to retain existing selections
55537      */
55538     selectRange : function(startRow, endRow, keepExisting){
55539         if(this.locked) return;
55540         if(!keepExisting){
55541             this.clearSelections();
55542         }
55543         if(startRow <= endRow){
55544             for(var i = startRow; i <= endRow; i++){
55545                 this.selectRow(i, true);
55546             }
55547         }else{
55548             for(var i = startRow; i >= endRow; i--){
55549                 this.selectRow(i, true);
55550             }
55551         }
55552     },
55553
55554     /**
55555      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55556      * @param {Number} startRow The index of the first row in the range
55557      * @param {Number} endRow The index of the last row in the range
55558      */
55559     deselectRange : function(startRow, endRow, preventViewNotify){
55560         if(this.locked) return;
55561         for(var i = startRow; i <= endRow; i++){
55562             this.deselectRow(i, preventViewNotify);
55563         }
55564     },
55565
55566     /**
55567      * Selects a row.
55568      * @param {Number} row The index of the row to select
55569      * @param {Boolean} keepExisting (optional) True to keep existing selections
55570      */
55571     selectRow : function(index, keepExisting, preventViewNotify){
55572         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55573         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55574             if(!keepExisting || this.singleSelect){
55575                 this.clearSelections();
55576             }
55577             var r = this.grid.dataSource.getAt(index);
55578             this.selections.add(r);
55579             this.last = this.lastActive = index;
55580             if(!preventViewNotify){
55581                 this.grid.getView().onRowSelect(index);
55582             }
55583             this.fireEvent("rowselect", this, index, r);
55584             this.fireEvent("selectionchange", this);
55585         }
55586     },
55587
55588     /**
55589      * Deselects a row.
55590      * @param {Number} row The index of the row to deselect
55591      */
55592     deselectRow : function(index, preventViewNotify){
55593         if(this.locked) return;
55594         if(this.last == index){
55595             this.last = false;
55596         }
55597         if(this.lastActive == index){
55598             this.lastActive = false;
55599         }
55600         var r = this.grid.dataSource.getAt(index);
55601         this.selections.remove(r);
55602         if(!preventViewNotify){
55603             this.grid.getView().onRowDeselect(index);
55604         }
55605         this.fireEvent("rowdeselect", this, index);
55606         this.fireEvent("selectionchange", this);
55607     },
55608
55609     // private
55610     restoreLast : function(){
55611         if(this._last){
55612             this.last = this._last;
55613         }
55614     },
55615
55616     // private
55617     acceptsNav : function(row, col, cm){
55618         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55619     },
55620
55621     // private
55622     onEditorKey : function(field, e){
55623         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55624         if(k == e.TAB){
55625             e.stopEvent();
55626             ed.completeEdit();
55627             if(e.shiftKey){
55628                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55629             }else{
55630                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55631             }
55632         }else if(k == e.ENTER && !e.ctrlKey){
55633             e.stopEvent();
55634             ed.completeEdit();
55635             if(e.shiftKey){
55636                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55637             }else{
55638                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55639             }
55640         }else if(k == e.ESC){
55641             ed.cancelEdit();
55642         }
55643         if(newCell){
55644             g.startEditing(newCell[0], newCell[1]);
55645         }
55646     }
55647 });/*
55648  * Based on:
55649  * Ext JS Library 1.1.1
55650  * Copyright(c) 2006-2007, Ext JS, LLC.
55651  *
55652  * Originally Released Under LGPL - original licence link has changed is not relivant.
55653  *
55654  * Fork - LGPL
55655  * <script type="text/javascript">
55656  */
55657 /**
55658  * @class Roo.grid.CellSelectionModel
55659  * @extends Roo.grid.AbstractSelectionModel
55660  * This class provides the basic implementation for cell selection in a grid.
55661  * @constructor
55662  * @param {Object} config The object containing the configuration of this model.
55663  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55664  */
55665 Roo.grid.CellSelectionModel = function(config){
55666     Roo.apply(this, config);
55667
55668     this.selection = null;
55669
55670     this.addEvents({
55671         /**
55672              * @event beforerowselect
55673              * Fires before a cell is selected.
55674              * @param {SelectionModel} this
55675              * @param {Number} rowIndex The selected row index
55676              * @param {Number} colIndex The selected cell index
55677              */
55678             "beforecellselect" : true,
55679         /**
55680              * @event cellselect
55681              * Fires when a cell is selected.
55682              * @param {SelectionModel} this
55683              * @param {Number} rowIndex The selected row index
55684              * @param {Number} colIndex The selected cell index
55685              */
55686             "cellselect" : true,
55687         /**
55688              * @event selectionchange
55689              * Fires when the active selection changes.
55690              * @param {SelectionModel} this
55691              * @param {Object} selection null for no selection or an object (o) with two properties
55692                 <ul>
55693                 <li>o.record: the record object for the row the selection is in</li>
55694                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55695                 </ul>
55696              */
55697             "selectionchange" : true,
55698         /**
55699              * @event tabend
55700              * Fires when the tab (or enter) was pressed on the last editable cell
55701              * You can use this to trigger add new row.
55702              * @param {SelectionModel} this
55703              */
55704             "tabend" : true,
55705          /**
55706              * @event beforeeditnext
55707              * Fires before the next editable sell is made active
55708              * You can use this to skip to another cell or fire the tabend
55709              *    if you set cell to false
55710              * @param {Object} eventdata object : { cell : [ row, col ] } 
55711              */
55712             "beforeeditnext" : true
55713     });
55714     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55715 };
55716
55717 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55718     
55719     enter_is_tab: false,
55720
55721     /** @ignore */
55722     initEvents : function(){
55723         this.grid.on("mousedown", this.handleMouseDown, this);
55724         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55725         var view = this.grid.view;
55726         view.on("refresh", this.onViewChange, this);
55727         view.on("rowupdated", this.onRowUpdated, this);
55728         view.on("beforerowremoved", this.clearSelections, this);
55729         view.on("beforerowsinserted", this.clearSelections, this);
55730         if(this.grid.isEditor){
55731             this.grid.on("beforeedit", this.beforeEdit,  this);
55732         }
55733     },
55734
55735         //private
55736     beforeEdit : function(e){
55737         this.select(e.row, e.column, false, true, e.record);
55738     },
55739
55740         //private
55741     onRowUpdated : function(v, index, r){
55742         if(this.selection && this.selection.record == r){
55743             v.onCellSelect(index, this.selection.cell[1]);
55744         }
55745     },
55746
55747         //private
55748     onViewChange : function(){
55749         this.clearSelections(true);
55750     },
55751
55752         /**
55753          * Returns the currently selected cell,.
55754          * @return {Array} The selected cell (row, column) or null if none selected.
55755          */
55756     getSelectedCell : function(){
55757         return this.selection ? this.selection.cell : null;
55758     },
55759
55760     /**
55761      * Clears all selections.
55762      * @param {Boolean} true to prevent the gridview from being notified about the change.
55763      */
55764     clearSelections : function(preventNotify){
55765         var s = this.selection;
55766         if(s){
55767             if(preventNotify !== true){
55768                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55769             }
55770             this.selection = null;
55771             this.fireEvent("selectionchange", this, null);
55772         }
55773     },
55774
55775     /**
55776      * Returns true if there is a selection.
55777      * @return {Boolean}
55778      */
55779     hasSelection : function(){
55780         return this.selection ? true : false;
55781     },
55782
55783     /** @ignore */
55784     handleMouseDown : function(e, t){
55785         var v = this.grid.getView();
55786         if(this.isLocked()){
55787             return;
55788         };
55789         var row = v.findRowIndex(t);
55790         var cell = v.findCellIndex(t);
55791         if(row !== false && cell !== false){
55792             this.select(row, cell);
55793         }
55794     },
55795
55796     /**
55797      * Selects a cell.
55798      * @param {Number} rowIndex
55799      * @param {Number} collIndex
55800      */
55801     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55802         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55803             this.clearSelections();
55804             r = r || this.grid.dataSource.getAt(rowIndex);
55805             this.selection = {
55806                 record : r,
55807                 cell : [rowIndex, colIndex]
55808             };
55809             if(!preventViewNotify){
55810                 var v = this.grid.getView();
55811                 v.onCellSelect(rowIndex, colIndex);
55812                 if(preventFocus !== true){
55813                     v.focusCell(rowIndex, colIndex);
55814                 }
55815             }
55816             this.fireEvent("cellselect", this, rowIndex, colIndex);
55817             this.fireEvent("selectionchange", this, this.selection);
55818         }
55819     },
55820
55821         //private
55822     isSelectable : function(rowIndex, colIndex, cm){
55823         return !cm.isHidden(colIndex);
55824     },
55825
55826     /** @ignore */
55827     handleKeyDown : function(e){
55828         //Roo.log('Cell Sel Model handleKeyDown');
55829         if(!e.isNavKeyPress()){
55830             return;
55831         }
55832         var g = this.grid, s = this.selection;
55833         if(!s){
55834             e.stopEvent();
55835             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55836             if(cell){
55837                 this.select(cell[0], cell[1]);
55838             }
55839             return;
55840         }
55841         var sm = this;
55842         var walk = function(row, col, step){
55843             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55844         };
55845         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55846         var newCell;
55847
55848       
55849
55850         switch(k){
55851             case e.TAB:
55852                 // handled by onEditorKey
55853                 if (g.isEditor && g.editing) {
55854                     return;
55855                 }
55856                 if(e.shiftKey) {
55857                     newCell = walk(r, c-1, -1);
55858                 } else {
55859                     newCell = walk(r, c+1, 1);
55860                 }
55861                 break;
55862             
55863             case e.DOWN:
55864                newCell = walk(r+1, c, 1);
55865                 break;
55866             
55867             case e.UP:
55868                 newCell = walk(r-1, c, -1);
55869                 break;
55870             
55871             case e.RIGHT:
55872                 newCell = walk(r, c+1, 1);
55873                 break;
55874             
55875             case e.LEFT:
55876                 newCell = walk(r, c-1, -1);
55877                 break;
55878             
55879             case e.ENTER:
55880                 
55881                 if(g.isEditor && !g.editing){
55882                    g.startEditing(r, c);
55883                    e.stopEvent();
55884                    return;
55885                 }
55886                 
55887                 
55888              break;
55889         };
55890         if(newCell){
55891             this.select(newCell[0], newCell[1]);
55892             e.stopEvent();
55893             
55894         }
55895     },
55896
55897     acceptsNav : function(row, col, cm){
55898         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55899     },
55900     /**
55901      * Selects a cell.
55902      * @param {Number} field (not used) - as it's normally used as a listener
55903      * @param {Number} e - event - fake it by using
55904      *
55905      * var e = Roo.EventObjectImpl.prototype;
55906      * e.keyCode = e.TAB
55907      *
55908      * 
55909      */
55910     onEditorKey : function(field, e){
55911         
55912         var k = e.getKey(),
55913             newCell,
55914             g = this.grid,
55915             ed = g.activeEditor,
55916             forward = false;
55917         ///Roo.log('onEditorKey' + k);
55918         
55919         
55920         if (this.enter_is_tab && k == e.ENTER) {
55921             k = e.TAB;
55922         }
55923         
55924         if(k == e.TAB){
55925             if(e.shiftKey){
55926                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55927             }else{
55928                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55929                 forward = true;
55930             }
55931             
55932             e.stopEvent();
55933             
55934         } else if(k == e.ENTER &&  !e.ctrlKey){
55935             ed.completeEdit();
55936             e.stopEvent();
55937             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55938         
55939                 } else if(k == e.ESC){
55940             ed.cancelEdit();
55941         }
55942                 
55943         if (newCell) {
55944             var ecall = { cell : newCell, forward : forward };
55945             this.fireEvent('beforeeditnext', ecall );
55946             newCell = ecall.cell;
55947                         forward = ecall.forward;
55948         }
55949                 
55950         if(newCell){
55951             //Roo.log('next cell after edit');
55952             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55953         } else if (forward) {
55954             // tabbed past last
55955             this.fireEvent.defer(100, this, ['tabend',this]);
55956         }
55957     }
55958 });/*
55959  * Based on:
55960  * Ext JS Library 1.1.1
55961  * Copyright(c) 2006-2007, Ext JS, LLC.
55962  *
55963  * Originally Released Under LGPL - original licence link has changed is not relivant.
55964  *
55965  * Fork - LGPL
55966  * <script type="text/javascript">
55967  */
55968  
55969 /**
55970  * @class Roo.grid.EditorGrid
55971  * @extends Roo.grid.Grid
55972  * Class for creating and editable grid.
55973  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55974  * The container MUST have some type of size defined for the grid to fill. The container will be 
55975  * automatically set to position relative if it isn't already.
55976  * @param {Object} dataSource The data model to bind to
55977  * @param {Object} colModel The column model with info about this grid's columns
55978  */
55979 Roo.grid.EditorGrid = function(container, config){
55980     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55981     this.getGridEl().addClass("xedit-grid");
55982
55983     if(!this.selModel){
55984         this.selModel = new Roo.grid.CellSelectionModel();
55985     }
55986
55987     this.activeEditor = null;
55988
55989         this.addEvents({
55990             /**
55991              * @event beforeedit
55992              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55993              * <ul style="padding:5px;padding-left:16px;">
55994              * <li>grid - This grid</li>
55995              * <li>record - The record being edited</li>
55996              * <li>field - The field name being edited</li>
55997              * <li>value - The value for the field being edited.</li>
55998              * <li>row - The grid row index</li>
55999              * <li>column - The grid column index</li>
56000              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56001              * </ul>
56002              * @param {Object} e An edit event (see above for description)
56003              */
56004             "beforeedit" : true,
56005             /**
56006              * @event afteredit
56007              * Fires after a cell is edited. <br />
56008              * <ul style="padding:5px;padding-left:16px;">
56009              * <li>grid - This grid</li>
56010              * <li>record - The record being edited</li>
56011              * <li>field - The field name being edited</li>
56012              * <li>value - The value being set</li>
56013              * <li>originalValue - The original value for the field, before the edit.</li>
56014              * <li>row - The grid row index</li>
56015              * <li>column - The grid column index</li>
56016              * </ul>
56017              * @param {Object} e An edit event (see above for description)
56018              */
56019             "afteredit" : true,
56020             /**
56021              * @event validateedit
56022              * Fires after a cell is edited, but before the value is set in the record. 
56023          * You can use this to modify the value being set in the field, Return false
56024              * to cancel the change. The edit event object has the following properties <br />
56025              * <ul style="padding:5px;padding-left:16px;">
56026          * <li>editor - This editor</li>
56027              * <li>grid - This grid</li>
56028              * <li>record - The record being edited</li>
56029              * <li>field - The field name being edited</li>
56030              * <li>value - The value being set</li>
56031              * <li>originalValue - The original value for the field, before the edit.</li>
56032              * <li>row - The grid row index</li>
56033              * <li>column - The grid column index</li>
56034              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56035              * </ul>
56036              * @param {Object} e An edit event (see above for description)
56037              */
56038             "validateedit" : true
56039         });
56040     this.on("bodyscroll", this.stopEditing,  this);
56041     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56042 };
56043
56044 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56045     /**
56046      * @cfg {Number} clicksToEdit
56047      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56048      */
56049     clicksToEdit: 2,
56050
56051     // private
56052     isEditor : true,
56053     // private
56054     trackMouseOver: false, // causes very odd FF errors
56055
56056     onCellDblClick : function(g, row, col){
56057         this.startEditing(row, col);
56058     },
56059
56060     onEditComplete : function(ed, value, startValue){
56061         this.editing = false;
56062         this.activeEditor = null;
56063         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56064         var r = ed.record;
56065         var field = this.colModel.getDataIndex(ed.col);
56066         var e = {
56067             grid: this,
56068             record: r,
56069             field: field,
56070             originalValue: startValue,
56071             value: value,
56072             row: ed.row,
56073             column: ed.col,
56074             cancel:false,
56075             editor: ed
56076         };
56077         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56078         cell.show();
56079           
56080         if(String(value) !== String(startValue)){
56081             
56082             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56083                 r.set(field, e.value);
56084                 // if we are dealing with a combo box..
56085                 // then we also set the 'name' colum to be the displayField
56086                 if (ed.field.displayField && ed.field.name) {
56087                     r.set(ed.field.name, ed.field.el.dom.value);
56088                 }
56089                 
56090                 delete e.cancel; //?? why!!!
56091                 this.fireEvent("afteredit", e);
56092             }
56093         } else {
56094             this.fireEvent("afteredit", e); // always fire it!
56095         }
56096         this.view.focusCell(ed.row, ed.col);
56097     },
56098
56099     /**
56100      * Starts editing the specified for the specified row/column
56101      * @param {Number} rowIndex
56102      * @param {Number} colIndex
56103      */
56104     startEditing : function(row, col){
56105         this.stopEditing();
56106         if(this.colModel.isCellEditable(col, row)){
56107             this.view.ensureVisible(row, col, true);
56108           
56109             var r = this.dataSource.getAt(row);
56110             var field = this.colModel.getDataIndex(col);
56111             var cell = Roo.get(this.view.getCell(row,col));
56112             var e = {
56113                 grid: this,
56114                 record: r,
56115                 field: field,
56116                 value: r.data[field],
56117                 row: row,
56118                 column: col,
56119                 cancel:false 
56120             };
56121             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56122                 this.editing = true;
56123                 var ed = this.colModel.getCellEditor(col, row);
56124                 
56125                 if (!ed) {
56126                     return;
56127                 }
56128                 if(!ed.rendered){
56129                     ed.render(ed.parentEl || document.body);
56130                 }
56131                 ed.field.reset();
56132                
56133                 cell.hide();
56134                 
56135                 (function(){ // complex but required for focus issues in safari, ie and opera
56136                     ed.row = row;
56137                     ed.col = col;
56138                     ed.record = r;
56139                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56140                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56141                     this.activeEditor = ed;
56142                     var v = r.data[field];
56143                     ed.startEdit(this.view.getCell(row, col), v);
56144                     // combo's with 'displayField and name set
56145                     if (ed.field.displayField && ed.field.name) {
56146                         ed.field.el.dom.value = r.data[ed.field.name];
56147                     }
56148                     
56149                     
56150                 }).defer(50, this);
56151             }
56152         }
56153     },
56154         
56155     /**
56156      * Stops any active editing
56157      */
56158     stopEditing : function(){
56159         if(this.activeEditor){
56160             this.activeEditor.completeEdit();
56161         }
56162         this.activeEditor = null;
56163     },
56164         
56165          /**
56166      * Called to get grid's drag proxy text, by default returns this.ddText.
56167      * @return {String}
56168      */
56169     getDragDropText : function(){
56170         var count = this.selModel.getSelectedCell() ? 1 : 0;
56171         return String.format(this.ddText, count, count == 1 ? '' : 's');
56172     }
56173         
56174 });/*
56175  * Based on:
56176  * Ext JS Library 1.1.1
56177  * Copyright(c) 2006-2007, Ext JS, LLC.
56178  *
56179  * Originally Released Under LGPL - original licence link has changed is not relivant.
56180  *
56181  * Fork - LGPL
56182  * <script type="text/javascript">
56183  */
56184
56185 // private - not really -- you end up using it !
56186 // This is a support class used internally by the Grid components
56187
56188 /**
56189  * @class Roo.grid.GridEditor
56190  * @extends Roo.Editor
56191  * Class for creating and editable grid elements.
56192  * @param {Object} config any settings (must include field)
56193  */
56194 Roo.grid.GridEditor = function(field, config){
56195     if (!config && field.field) {
56196         config = field;
56197         field = Roo.factory(config.field, Roo.form);
56198     }
56199     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56200     field.monitorTab = false;
56201 };
56202
56203 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56204     
56205     /**
56206      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56207      */
56208     
56209     alignment: "tl-tl",
56210     autoSize: "width",
56211     hideEl : false,
56212     cls: "x-small-editor x-grid-editor",
56213     shim:false,
56214     shadow:"frame"
56215 });/*
56216  * Based on:
56217  * Ext JS Library 1.1.1
56218  * Copyright(c) 2006-2007, Ext JS, LLC.
56219  *
56220  * Originally Released Under LGPL - original licence link has changed is not relivant.
56221  *
56222  * Fork - LGPL
56223  * <script type="text/javascript">
56224  */
56225   
56226
56227   
56228 Roo.grid.PropertyRecord = Roo.data.Record.create([
56229     {name:'name',type:'string'},  'value'
56230 ]);
56231
56232
56233 Roo.grid.PropertyStore = function(grid, source){
56234     this.grid = grid;
56235     this.store = new Roo.data.Store({
56236         recordType : Roo.grid.PropertyRecord
56237     });
56238     this.store.on('update', this.onUpdate,  this);
56239     if(source){
56240         this.setSource(source);
56241     }
56242     Roo.grid.PropertyStore.superclass.constructor.call(this);
56243 };
56244
56245
56246
56247 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56248     setSource : function(o){
56249         this.source = o;
56250         this.store.removeAll();
56251         var data = [];
56252         for(var k in o){
56253             if(this.isEditableValue(o[k])){
56254                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56255             }
56256         }
56257         this.store.loadRecords({records: data}, {}, true);
56258     },
56259
56260     onUpdate : function(ds, record, type){
56261         if(type == Roo.data.Record.EDIT){
56262             var v = record.data['value'];
56263             var oldValue = record.modified['value'];
56264             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56265                 this.source[record.id] = v;
56266                 record.commit();
56267                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56268             }else{
56269                 record.reject();
56270             }
56271         }
56272     },
56273
56274     getProperty : function(row){
56275        return this.store.getAt(row);
56276     },
56277
56278     isEditableValue: function(val){
56279         if(val && val instanceof Date){
56280             return true;
56281         }else if(typeof val == 'object' || typeof val == 'function'){
56282             return false;
56283         }
56284         return true;
56285     },
56286
56287     setValue : function(prop, value){
56288         this.source[prop] = value;
56289         this.store.getById(prop).set('value', value);
56290     },
56291
56292     getSource : function(){
56293         return this.source;
56294     }
56295 });
56296
56297 Roo.grid.PropertyColumnModel = function(grid, store){
56298     this.grid = grid;
56299     var g = Roo.grid;
56300     g.PropertyColumnModel.superclass.constructor.call(this, [
56301         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56302         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56303     ]);
56304     this.store = store;
56305     this.bselect = Roo.DomHelper.append(document.body, {
56306         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56307             {tag: 'option', value: 'true', html: 'true'},
56308             {tag: 'option', value: 'false', html: 'false'}
56309         ]
56310     });
56311     Roo.id(this.bselect);
56312     var f = Roo.form;
56313     this.editors = {
56314         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56315         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56316         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56317         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56318         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56319     };
56320     this.renderCellDelegate = this.renderCell.createDelegate(this);
56321     this.renderPropDelegate = this.renderProp.createDelegate(this);
56322 };
56323
56324 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56325     
56326     
56327     nameText : 'Name',
56328     valueText : 'Value',
56329     
56330     dateFormat : 'm/j/Y',
56331     
56332     
56333     renderDate : function(dateVal){
56334         return dateVal.dateFormat(this.dateFormat);
56335     },
56336
56337     renderBool : function(bVal){
56338         return bVal ? 'true' : 'false';
56339     },
56340
56341     isCellEditable : function(colIndex, rowIndex){
56342         return colIndex == 1;
56343     },
56344
56345     getRenderer : function(col){
56346         return col == 1 ?
56347             this.renderCellDelegate : this.renderPropDelegate;
56348     },
56349
56350     renderProp : function(v){
56351         return this.getPropertyName(v);
56352     },
56353
56354     renderCell : function(val){
56355         var rv = val;
56356         if(val instanceof Date){
56357             rv = this.renderDate(val);
56358         }else if(typeof val == 'boolean'){
56359             rv = this.renderBool(val);
56360         }
56361         return Roo.util.Format.htmlEncode(rv);
56362     },
56363
56364     getPropertyName : function(name){
56365         var pn = this.grid.propertyNames;
56366         return pn && pn[name] ? pn[name] : name;
56367     },
56368
56369     getCellEditor : function(colIndex, rowIndex){
56370         var p = this.store.getProperty(rowIndex);
56371         var n = p.data['name'], val = p.data['value'];
56372         
56373         if(typeof(this.grid.customEditors[n]) == 'string'){
56374             return this.editors[this.grid.customEditors[n]];
56375         }
56376         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56377             return this.grid.customEditors[n];
56378         }
56379         if(val instanceof Date){
56380             return this.editors['date'];
56381         }else if(typeof val == 'number'){
56382             return this.editors['number'];
56383         }else if(typeof val == 'boolean'){
56384             return this.editors['boolean'];
56385         }else{
56386             return this.editors['string'];
56387         }
56388     }
56389 });
56390
56391 /**
56392  * @class Roo.grid.PropertyGrid
56393  * @extends Roo.grid.EditorGrid
56394  * This class represents the  interface of a component based property grid control.
56395  * <br><br>Usage:<pre><code>
56396  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56397       
56398  });
56399  // set any options
56400  grid.render();
56401  * </code></pre>
56402   
56403  * @constructor
56404  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56405  * The container MUST have some type of size defined for the grid to fill. The container will be
56406  * automatically set to position relative if it isn't already.
56407  * @param {Object} config A config object that sets properties on this grid.
56408  */
56409 Roo.grid.PropertyGrid = function(container, config){
56410     config = config || {};
56411     var store = new Roo.grid.PropertyStore(this);
56412     this.store = store;
56413     var cm = new Roo.grid.PropertyColumnModel(this, store);
56414     store.store.sort('name', 'ASC');
56415     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56416         ds: store.store,
56417         cm: cm,
56418         enableColLock:false,
56419         enableColumnMove:false,
56420         stripeRows:false,
56421         trackMouseOver: false,
56422         clicksToEdit:1
56423     }, config));
56424     this.getGridEl().addClass('x-props-grid');
56425     this.lastEditRow = null;
56426     this.on('columnresize', this.onColumnResize, this);
56427     this.addEvents({
56428          /**
56429              * @event beforepropertychange
56430              * Fires before a property changes (return false to stop?)
56431              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56432              * @param {String} id Record Id
56433              * @param {String} newval New Value
56434          * @param {String} oldval Old Value
56435              */
56436         "beforepropertychange": true,
56437         /**
56438              * @event propertychange
56439              * Fires after a property changes
56440              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56441              * @param {String} id Record Id
56442              * @param {String} newval New Value
56443          * @param {String} oldval Old Value
56444              */
56445         "propertychange": true
56446     });
56447     this.customEditors = this.customEditors || {};
56448 };
56449 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56450     
56451      /**
56452      * @cfg {Object} customEditors map of colnames=> custom editors.
56453      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56454      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56455      * false disables editing of the field.
56456          */
56457     
56458       /**
56459      * @cfg {Object} propertyNames map of property Names to their displayed value
56460          */
56461     
56462     render : function(){
56463         Roo.grid.PropertyGrid.superclass.render.call(this);
56464         this.autoSize.defer(100, this);
56465     },
56466
56467     autoSize : function(){
56468         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56469         if(this.view){
56470             this.view.fitColumns();
56471         }
56472     },
56473
56474     onColumnResize : function(){
56475         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56476         this.autoSize();
56477     },
56478     /**
56479      * Sets the data for the Grid
56480      * accepts a Key => Value object of all the elements avaiable.
56481      * @param {Object} data  to appear in grid.
56482      */
56483     setSource : function(source){
56484         this.store.setSource(source);
56485         //this.autoSize();
56486     },
56487     /**
56488      * Gets all the data from the grid.
56489      * @return {Object} data  data stored in grid
56490      */
56491     getSource : function(){
56492         return this.store.getSource();
56493     }
56494 });/*
56495   
56496  * Licence LGPL
56497  
56498  */
56499  
56500 /**
56501  * @class Roo.grid.Calendar
56502  * @extends Roo.util.Grid
56503  * This class extends the Grid to provide a calendar widget
56504  * <br><br>Usage:<pre><code>
56505  var grid = new Roo.grid.Calendar("my-container-id", {
56506      ds: myDataStore,
56507      cm: myColModel,
56508      selModel: mySelectionModel,
56509      autoSizeColumns: true,
56510      monitorWindowResize: false,
56511      trackMouseOver: true
56512      eventstore : real data store..
56513  });
56514  // set any options
56515  grid.render();
56516   
56517   * @constructor
56518  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56519  * The container MUST have some type of size defined for the grid to fill. The container will be
56520  * automatically set to position relative if it isn't already.
56521  * @param {Object} config A config object that sets properties on this grid.
56522  */
56523 Roo.grid.Calendar = function(container, config){
56524         // initialize the container
56525         this.container = Roo.get(container);
56526         this.container.update("");
56527         this.container.setStyle("overflow", "hidden");
56528     this.container.addClass('x-grid-container');
56529
56530     this.id = this.container.id;
56531
56532     Roo.apply(this, config);
56533     // check and correct shorthanded configs
56534     
56535     var rows = [];
56536     var d =1;
56537     for (var r = 0;r < 6;r++) {
56538         
56539         rows[r]=[];
56540         for (var c =0;c < 7;c++) {
56541             rows[r][c]= '';
56542         }
56543     }
56544     if (this.eventStore) {
56545         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56546         this.eventStore.on('load',this.onLoad, this);
56547         this.eventStore.on('beforeload',this.clearEvents, this);
56548          
56549     }
56550     
56551     this.dataSource = new Roo.data.Store({
56552             proxy: new Roo.data.MemoryProxy(rows),
56553             reader: new Roo.data.ArrayReader({}, [
56554                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56555     });
56556
56557     this.dataSource.load();
56558     this.ds = this.dataSource;
56559     this.ds.xmodule = this.xmodule || false;
56560     
56561     
56562     var cellRender = function(v,x,r)
56563     {
56564         return String.format(
56565             '<div class="fc-day  fc-widget-content"><div>' +
56566                 '<div class="fc-event-container"></div>' +
56567                 '<div class="fc-day-number">{0}</div>'+
56568                 
56569                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56570             '</div></div>', v);
56571     
56572     }
56573     
56574     
56575     this.colModel = new Roo.grid.ColumnModel( [
56576         {
56577             xtype: 'ColumnModel',
56578             xns: Roo.grid,
56579             dataIndex : 'weekday0',
56580             header : 'Sunday',
56581             renderer : cellRender
56582         },
56583         {
56584             xtype: 'ColumnModel',
56585             xns: Roo.grid,
56586             dataIndex : 'weekday1',
56587             header : 'Monday',
56588             renderer : cellRender
56589         },
56590         {
56591             xtype: 'ColumnModel',
56592             xns: Roo.grid,
56593             dataIndex : 'weekday2',
56594             header : 'Tuesday',
56595             renderer : cellRender
56596         },
56597         {
56598             xtype: 'ColumnModel',
56599             xns: Roo.grid,
56600             dataIndex : 'weekday3',
56601             header : 'Wednesday',
56602             renderer : cellRender
56603         },
56604         {
56605             xtype: 'ColumnModel',
56606             xns: Roo.grid,
56607             dataIndex : 'weekday4',
56608             header : 'Thursday',
56609             renderer : cellRender
56610         },
56611         {
56612             xtype: 'ColumnModel',
56613             xns: Roo.grid,
56614             dataIndex : 'weekday5',
56615             header : 'Friday',
56616             renderer : cellRender
56617         },
56618         {
56619             xtype: 'ColumnModel',
56620             xns: Roo.grid,
56621             dataIndex : 'weekday6',
56622             header : 'Saturday',
56623             renderer : cellRender
56624         }
56625     ]);
56626     this.cm = this.colModel;
56627     this.cm.xmodule = this.xmodule || false;
56628  
56629         
56630           
56631     //this.selModel = new Roo.grid.CellSelectionModel();
56632     //this.sm = this.selModel;
56633     //this.selModel.init(this);
56634     
56635     
56636     if(this.width){
56637         this.container.setWidth(this.width);
56638     }
56639
56640     if(this.height){
56641         this.container.setHeight(this.height);
56642     }
56643     /** @private */
56644         this.addEvents({
56645         // raw events
56646         /**
56647          * @event click
56648          * The raw click event for the entire grid.
56649          * @param {Roo.EventObject} e
56650          */
56651         "click" : true,
56652         /**
56653          * @event dblclick
56654          * The raw dblclick event for the entire grid.
56655          * @param {Roo.EventObject} e
56656          */
56657         "dblclick" : true,
56658         /**
56659          * @event contextmenu
56660          * The raw contextmenu event for the entire grid.
56661          * @param {Roo.EventObject} e
56662          */
56663         "contextmenu" : true,
56664         /**
56665          * @event mousedown
56666          * The raw mousedown event for the entire grid.
56667          * @param {Roo.EventObject} e
56668          */
56669         "mousedown" : true,
56670         /**
56671          * @event mouseup
56672          * The raw mouseup event for the entire grid.
56673          * @param {Roo.EventObject} e
56674          */
56675         "mouseup" : true,
56676         /**
56677          * @event mouseover
56678          * The raw mouseover event for the entire grid.
56679          * @param {Roo.EventObject} e
56680          */
56681         "mouseover" : true,
56682         /**
56683          * @event mouseout
56684          * The raw mouseout event for the entire grid.
56685          * @param {Roo.EventObject} e
56686          */
56687         "mouseout" : true,
56688         /**
56689          * @event keypress
56690          * The raw keypress event for the entire grid.
56691          * @param {Roo.EventObject} e
56692          */
56693         "keypress" : true,
56694         /**
56695          * @event keydown
56696          * The raw keydown event for the entire grid.
56697          * @param {Roo.EventObject} e
56698          */
56699         "keydown" : true,
56700
56701         // custom events
56702
56703         /**
56704          * @event cellclick
56705          * Fires when a cell is clicked
56706          * @param {Grid} this
56707          * @param {Number} rowIndex
56708          * @param {Number} columnIndex
56709          * @param {Roo.EventObject} e
56710          */
56711         "cellclick" : true,
56712         /**
56713          * @event celldblclick
56714          * Fires when a cell is double clicked
56715          * @param {Grid} this
56716          * @param {Number} rowIndex
56717          * @param {Number} columnIndex
56718          * @param {Roo.EventObject} e
56719          */
56720         "celldblclick" : true,
56721         /**
56722          * @event rowclick
56723          * Fires when a row is clicked
56724          * @param {Grid} this
56725          * @param {Number} rowIndex
56726          * @param {Roo.EventObject} e
56727          */
56728         "rowclick" : true,
56729         /**
56730          * @event rowdblclick
56731          * Fires when a row is double clicked
56732          * @param {Grid} this
56733          * @param {Number} rowIndex
56734          * @param {Roo.EventObject} e
56735          */
56736         "rowdblclick" : true,
56737         /**
56738          * @event headerclick
56739          * Fires when a header is clicked
56740          * @param {Grid} this
56741          * @param {Number} columnIndex
56742          * @param {Roo.EventObject} e
56743          */
56744         "headerclick" : true,
56745         /**
56746          * @event headerdblclick
56747          * Fires when a header cell is double clicked
56748          * @param {Grid} this
56749          * @param {Number} columnIndex
56750          * @param {Roo.EventObject} e
56751          */
56752         "headerdblclick" : true,
56753         /**
56754          * @event rowcontextmenu
56755          * Fires when a row is right clicked
56756          * @param {Grid} this
56757          * @param {Number} rowIndex
56758          * @param {Roo.EventObject} e
56759          */
56760         "rowcontextmenu" : true,
56761         /**
56762          * @event cellcontextmenu
56763          * Fires when a cell is right clicked
56764          * @param {Grid} this
56765          * @param {Number} rowIndex
56766          * @param {Number} cellIndex
56767          * @param {Roo.EventObject} e
56768          */
56769          "cellcontextmenu" : true,
56770         /**
56771          * @event headercontextmenu
56772          * Fires when a header is right clicked
56773          * @param {Grid} this
56774          * @param {Number} columnIndex
56775          * @param {Roo.EventObject} e
56776          */
56777         "headercontextmenu" : true,
56778         /**
56779          * @event bodyscroll
56780          * Fires when the body element is scrolled
56781          * @param {Number} scrollLeft
56782          * @param {Number} scrollTop
56783          */
56784         "bodyscroll" : true,
56785         /**
56786          * @event columnresize
56787          * Fires when the user resizes a column
56788          * @param {Number} columnIndex
56789          * @param {Number} newSize
56790          */
56791         "columnresize" : true,
56792         /**
56793          * @event columnmove
56794          * Fires when the user moves a column
56795          * @param {Number} oldIndex
56796          * @param {Number} newIndex
56797          */
56798         "columnmove" : true,
56799         /**
56800          * @event startdrag
56801          * Fires when row(s) start being dragged
56802          * @param {Grid} this
56803          * @param {Roo.GridDD} dd The drag drop object
56804          * @param {event} e The raw browser event
56805          */
56806         "startdrag" : true,
56807         /**
56808          * @event enddrag
56809          * Fires when a drag operation is complete
56810          * @param {Grid} this
56811          * @param {Roo.GridDD} dd The drag drop object
56812          * @param {event} e The raw browser event
56813          */
56814         "enddrag" : true,
56815         /**
56816          * @event dragdrop
56817          * Fires when dragged row(s) are dropped on a valid DD target
56818          * @param {Grid} this
56819          * @param {Roo.GridDD} dd The drag drop object
56820          * @param {String} targetId The target drag drop object
56821          * @param {event} e The raw browser event
56822          */
56823         "dragdrop" : true,
56824         /**
56825          * @event dragover
56826          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56827          * @param {Grid} this
56828          * @param {Roo.GridDD} dd The drag drop object
56829          * @param {String} targetId The target drag drop object
56830          * @param {event} e The raw browser event
56831          */
56832         "dragover" : true,
56833         /**
56834          * @event dragenter
56835          *  Fires when the dragged row(s) first cross another DD target while being dragged
56836          * @param {Grid} this
56837          * @param {Roo.GridDD} dd The drag drop object
56838          * @param {String} targetId The target drag drop object
56839          * @param {event} e The raw browser event
56840          */
56841         "dragenter" : true,
56842         /**
56843          * @event dragout
56844          * Fires when the dragged row(s) leave another DD target while being dragged
56845          * @param {Grid} this
56846          * @param {Roo.GridDD} dd The drag drop object
56847          * @param {String} targetId The target drag drop object
56848          * @param {event} e The raw browser event
56849          */
56850         "dragout" : true,
56851         /**
56852          * @event rowclass
56853          * Fires when a row is rendered, so you can change add a style to it.
56854          * @param {GridView} gridview   The grid view
56855          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56856          */
56857         'rowclass' : true,
56858
56859         /**
56860          * @event render
56861          * Fires when the grid is rendered
56862          * @param {Grid} grid
56863          */
56864         'render' : true,
56865             /**
56866              * @event select
56867              * Fires when a date is selected
56868              * @param {DatePicker} this
56869              * @param {Date} date The selected date
56870              */
56871         'select': true,
56872         /**
56873              * @event monthchange
56874              * Fires when the displayed month changes 
56875              * @param {DatePicker} this
56876              * @param {Date} date The selected month
56877              */
56878         'monthchange': true,
56879         /**
56880              * @event evententer
56881              * Fires when mouse over an event
56882              * @param {Calendar} this
56883              * @param {event} Event
56884              */
56885         'evententer': true,
56886         /**
56887              * @event eventleave
56888              * Fires when the mouse leaves an
56889              * @param {Calendar} this
56890              * @param {event}
56891              */
56892         'eventleave': true,
56893         /**
56894              * @event eventclick
56895              * Fires when the mouse click an
56896              * @param {Calendar} this
56897              * @param {event}
56898              */
56899         'eventclick': true,
56900         /**
56901              * @event eventrender
56902              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56903              * @param {Calendar} this
56904              * @param {data} data to be modified
56905              */
56906         'eventrender': true
56907         
56908     });
56909
56910     Roo.grid.Grid.superclass.constructor.call(this);
56911     this.on('render', function() {
56912         this.view.el.addClass('x-grid-cal'); 
56913         
56914         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56915
56916     },this);
56917     
56918     if (!Roo.grid.Calendar.style) {
56919         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56920             
56921             
56922             '.x-grid-cal .x-grid-col' :  {
56923                 height: 'auto !important',
56924                 'vertical-align': 'top'
56925             },
56926             '.x-grid-cal  .fc-event-hori' : {
56927                 height: '14px'
56928             }
56929              
56930             
56931         }, Roo.id());
56932     }
56933
56934     
56935     
56936 };
56937 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56938     /**
56939      * @cfg {Store} eventStore The store that loads events.
56940      */
56941     eventStore : 25,
56942
56943      
56944     activeDate : false,
56945     startDay : 0,
56946     autoWidth : true,
56947     monitorWindowResize : false,
56948
56949     
56950     resizeColumns : function() {
56951         var col = (this.view.el.getWidth() / 7) - 3;
56952         // loop through cols, and setWidth
56953         for(var i =0 ; i < 7 ; i++){
56954             this.cm.setColumnWidth(i, col);
56955         }
56956     },
56957      setDate :function(date) {
56958         
56959         Roo.log('setDate?');
56960         
56961         this.resizeColumns();
56962         var vd = this.activeDate;
56963         this.activeDate = date;
56964 //        if(vd && this.el){
56965 //            var t = date.getTime();
56966 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56967 //                Roo.log('using add remove');
56968 //                
56969 //                this.fireEvent('monthchange', this, date);
56970 //                
56971 //                this.cells.removeClass("fc-state-highlight");
56972 //                this.cells.each(function(c){
56973 //                   if(c.dateValue == t){
56974 //                       c.addClass("fc-state-highlight");
56975 //                       setTimeout(function(){
56976 //                            try{c.dom.firstChild.focus();}catch(e){}
56977 //                       }, 50);
56978 //                       return false;
56979 //                   }
56980 //                   return true;
56981 //                });
56982 //                return;
56983 //            }
56984 //        }
56985         
56986         var days = date.getDaysInMonth();
56987         
56988         var firstOfMonth = date.getFirstDateOfMonth();
56989         var startingPos = firstOfMonth.getDay()-this.startDay;
56990         
56991         if(startingPos < this.startDay){
56992             startingPos += 7;
56993         }
56994         
56995         var pm = date.add(Date.MONTH, -1);
56996         var prevStart = pm.getDaysInMonth()-startingPos;
56997 //        
56998         
56999         
57000         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57001         
57002         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57003         //this.cells.addClassOnOver('fc-state-hover');
57004         
57005         var cells = this.cells.elements;
57006         var textEls = this.textNodes;
57007         
57008         //Roo.each(cells, function(cell){
57009         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57010         //});
57011         
57012         days += startingPos;
57013
57014         // convert everything to numbers so it's fast
57015         var day = 86400000;
57016         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57017         //Roo.log(d);
57018         //Roo.log(pm);
57019         //Roo.log(prevStart);
57020         
57021         var today = new Date().clearTime().getTime();
57022         var sel = date.clearTime().getTime();
57023         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57024         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57025         var ddMatch = this.disabledDatesRE;
57026         var ddText = this.disabledDatesText;
57027         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57028         var ddaysText = this.disabledDaysText;
57029         var format = this.format;
57030         
57031         var setCellClass = function(cal, cell){
57032             
57033             //Roo.log('set Cell Class');
57034             cell.title = "";
57035             var t = d.getTime();
57036             
57037             //Roo.log(d);
57038             
57039             
57040             cell.dateValue = t;
57041             if(t == today){
57042                 cell.className += " fc-today";
57043                 cell.className += " fc-state-highlight";
57044                 cell.title = cal.todayText;
57045             }
57046             if(t == sel){
57047                 // disable highlight in other month..
57048                 cell.className += " fc-state-highlight";
57049                 
57050             }
57051             // disabling
57052             if(t < min) {
57053                 //cell.className = " fc-state-disabled";
57054                 cell.title = cal.minText;
57055                 return;
57056             }
57057             if(t > max) {
57058                 //cell.className = " fc-state-disabled";
57059                 cell.title = cal.maxText;
57060                 return;
57061             }
57062             if(ddays){
57063                 if(ddays.indexOf(d.getDay()) != -1){
57064                     // cell.title = ddaysText;
57065                    // cell.className = " fc-state-disabled";
57066                 }
57067             }
57068             if(ddMatch && format){
57069                 var fvalue = d.dateFormat(format);
57070                 if(ddMatch.test(fvalue)){
57071                     cell.title = ddText.replace("%0", fvalue);
57072                    cell.className = " fc-state-disabled";
57073                 }
57074             }
57075             
57076             if (!cell.initialClassName) {
57077                 cell.initialClassName = cell.dom.className;
57078             }
57079             
57080             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57081         };
57082
57083         var i = 0;
57084         
57085         for(; i < startingPos; i++) {
57086             cells[i].dayName =  (++prevStart);
57087             Roo.log(textEls[i]);
57088             d.setDate(d.getDate()+1);
57089             
57090             //cells[i].className = "fc-past fc-other-month";
57091             setCellClass(this, cells[i]);
57092         }
57093         
57094         var intDay = 0;
57095         
57096         for(; i < days; i++){
57097             intDay = i - startingPos + 1;
57098             cells[i].dayName =  (intDay);
57099             d.setDate(d.getDate()+1);
57100             
57101             cells[i].className = ''; // "x-date-active";
57102             setCellClass(this, cells[i]);
57103         }
57104         var extraDays = 0;
57105         
57106         for(; i < 42; i++) {
57107             //textEls[i].innerHTML = (++extraDays);
57108             
57109             d.setDate(d.getDate()+1);
57110             cells[i].dayName = (++extraDays);
57111             cells[i].className = "fc-future fc-other-month";
57112             setCellClass(this, cells[i]);
57113         }
57114         
57115         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57116         
57117         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57118         
57119         // this will cause all the cells to mis
57120         var rows= [];
57121         var i =0;
57122         for (var r = 0;r < 6;r++) {
57123             for (var c =0;c < 7;c++) {
57124                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57125             }    
57126         }
57127         
57128         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57129         for(i=0;i<cells.length;i++) {
57130             
57131             this.cells.elements[i].dayName = cells[i].dayName ;
57132             this.cells.elements[i].className = cells[i].className;
57133             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57134             this.cells.elements[i].title = cells[i].title ;
57135             this.cells.elements[i].dateValue = cells[i].dateValue ;
57136         }
57137         
57138         
57139         
57140         
57141         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57142         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57143         
57144         ////if(totalRows != 6){
57145             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57146            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57147        // }
57148         
57149         this.fireEvent('monthchange', this, date);
57150         
57151         
57152     },
57153  /**
57154      * Returns the grid's SelectionModel.
57155      * @return {SelectionModel}
57156      */
57157     getSelectionModel : function(){
57158         if(!this.selModel){
57159             this.selModel = new Roo.grid.CellSelectionModel();
57160         }
57161         return this.selModel;
57162     },
57163
57164     load: function() {
57165         this.eventStore.load()
57166         
57167         
57168         
57169     },
57170     
57171     findCell : function(dt) {
57172         dt = dt.clearTime().getTime();
57173         var ret = false;
57174         this.cells.each(function(c){
57175             //Roo.log("check " +c.dateValue + '?=' + dt);
57176             if(c.dateValue == dt){
57177                 ret = c;
57178                 return false;
57179             }
57180             return true;
57181         });
57182         
57183         return ret;
57184     },
57185     
57186     findCells : function(rec) {
57187         var s = rec.data.start_dt.clone().clearTime().getTime();
57188        // Roo.log(s);
57189         var e= rec.data.end_dt.clone().clearTime().getTime();
57190        // Roo.log(e);
57191         var ret = [];
57192         this.cells.each(function(c){
57193              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57194             
57195             if(c.dateValue > e){
57196                 return ;
57197             }
57198             if(c.dateValue < s){
57199                 return ;
57200             }
57201             ret.push(c);
57202         });
57203         
57204         return ret;    
57205     },
57206     
57207     findBestRow: function(cells)
57208     {
57209         var ret = 0;
57210         
57211         for (var i =0 ; i < cells.length;i++) {
57212             ret  = Math.max(cells[i].rows || 0,ret);
57213         }
57214         return ret;
57215         
57216     },
57217     
57218     
57219     addItem : function(rec)
57220     {
57221         // look for vertical location slot in
57222         var cells = this.findCells(rec);
57223         
57224         rec.row = this.findBestRow(cells);
57225         
57226         // work out the location.
57227         
57228         var crow = false;
57229         var rows = [];
57230         for(var i =0; i < cells.length; i++) {
57231             if (!crow) {
57232                 crow = {
57233                     start : cells[i],
57234                     end :  cells[i]
57235                 };
57236                 continue;
57237             }
57238             if (crow.start.getY() == cells[i].getY()) {
57239                 // on same row.
57240                 crow.end = cells[i];
57241                 continue;
57242             }
57243             // different row.
57244             rows.push(crow);
57245             crow = {
57246                 start: cells[i],
57247                 end : cells[i]
57248             };
57249             
57250         }
57251         
57252         rows.push(crow);
57253         rec.els = [];
57254         rec.rows = rows;
57255         rec.cells = cells;
57256         for (var i = 0; i < cells.length;i++) {
57257             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57258             
57259         }
57260         
57261         
57262     },
57263     
57264     clearEvents: function() {
57265         
57266         if (!this.eventStore.getCount()) {
57267             return;
57268         }
57269         // reset number of rows in cells.
57270         Roo.each(this.cells.elements, function(c){
57271             c.rows = 0;
57272         });
57273         
57274         this.eventStore.each(function(e) {
57275             this.clearEvent(e);
57276         },this);
57277         
57278     },
57279     
57280     clearEvent : function(ev)
57281     {
57282         if (ev.els) {
57283             Roo.each(ev.els, function(el) {
57284                 el.un('mouseenter' ,this.onEventEnter, this);
57285                 el.un('mouseleave' ,this.onEventLeave, this);
57286                 el.remove();
57287             },this);
57288             ev.els = [];
57289         }
57290     },
57291     
57292     
57293     renderEvent : function(ev,ctr) {
57294         if (!ctr) {
57295              ctr = this.view.el.select('.fc-event-container',true).first();
57296         }
57297         
57298          
57299         this.clearEvent(ev);
57300             //code
57301        
57302         
57303         
57304         ev.els = [];
57305         var cells = ev.cells;
57306         var rows = ev.rows;
57307         this.fireEvent('eventrender', this, ev);
57308         
57309         for(var i =0; i < rows.length; i++) {
57310             
57311             cls = '';
57312             if (i == 0) {
57313                 cls += ' fc-event-start';
57314             }
57315             if ((i+1) == rows.length) {
57316                 cls += ' fc-event-end';
57317             }
57318             
57319             //Roo.log(ev.data);
57320             // how many rows should it span..
57321             var cg = this.eventTmpl.append(ctr,Roo.apply({
57322                 fccls : cls
57323                 
57324             }, ev.data) , true);
57325             
57326             
57327             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57328             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57329             cg.on('click', this.onEventClick, this, ev);
57330             
57331             ev.els.push(cg);
57332             
57333             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57334             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57335             //Roo.log(cg);
57336              
57337             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57338             cg.setWidth(ebox.right - sbox.x -2);
57339         }
57340     },
57341     
57342     renderEvents: function()
57343     {   
57344         // first make sure there is enough space..
57345         
57346         if (!this.eventTmpl) {
57347             this.eventTmpl = new Roo.Template(
57348                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57349                     '<div class="fc-event-inner">' +
57350                         '<span class="fc-event-time">{time}</span>' +
57351                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57352                     '</div>' +
57353                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57354                 '</div>'
57355             );
57356                 
57357         }
57358                
57359         
57360         
57361         this.cells.each(function(c) {
57362             //Roo.log(c.select('.fc-day-content div',true).first());
57363             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57364         });
57365         
57366         var ctr = this.view.el.select('.fc-event-container',true).first();
57367         
57368         var cls;
57369         this.eventStore.each(function(ev){
57370             
57371             this.renderEvent(ev);
57372              
57373              
57374         }, this);
57375         this.view.layout();
57376         
57377     },
57378     
57379     onEventEnter: function (e, el,event,d) {
57380         this.fireEvent('evententer', this, el, event);
57381     },
57382     
57383     onEventLeave: function (e, el,event,d) {
57384         this.fireEvent('eventleave', this, el, event);
57385     },
57386     
57387     onEventClick: function (e, el,event,d) {
57388         this.fireEvent('eventclick', this, el, event);
57389     },
57390     
57391     onMonthChange: function () {
57392         this.store.load();
57393     },
57394     
57395     onLoad: function () {
57396         
57397         //Roo.log('calendar onload');
57398 //         
57399         if(this.eventStore.getCount() > 0){
57400             
57401            
57402             
57403             this.eventStore.each(function(d){
57404                 
57405                 
57406                 // FIXME..
57407                 var add =   d.data;
57408                 if (typeof(add.end_dt) == 'undefined')  {
57409                     Roo.log("Missing End time in calendar data: ");
57410                     Roo.log(d);
57411                     return;
57412                 }
57413                 if (typeof(add.start_dt) == 'undefined')  {
57414                     Roo.log("Missing Start time in calendar data: ");
57415                     Roo.log(d);
57416                     return;
57417                 }
57418                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57419                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57420                 add.id = add.id || d.id;
57421                 add.title = add.title || '??';
57422                 
57423                 this.addItem(d);
57424                 
57425              
57426             },this);
57427         }
57428         
57429         this.renderEvents();
57430     }
57431     
57432
57433 });
57434 /*
57435  grid : {
57436                 xtype: 'Grid',
57437                 xns: Roo.grid,
57438                 listeners : {
57439                     render : function ()
57440                     {
57441                         _this.grid = this;
57442                         
57443                         if (!this.view.el.hasClass('course-timesheet')) {
57444                             this.view.el.addClass('course-timesheet');
57445                         }
57446                         if (this.tsStyle) {
57447                             this.ds.load({});
57448                             return; 
57449                         }
57450                         Roo.log('width');
57451                         Roo.log(_this.grid.view.el.getWidth());
57452                         
57453                         
57454                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57455                             '.course-timesheet .x-grid-row' : {
57456                                 height: '80px'
57457                             },
57458                             '.x-grid-row td' : {
57459                                 'vertical-align' : 0
57460                             },
57461                             '.course-edit-link' : {
57462                                 'color' : 'blue',
57463                                 'text-overflow' : 'ellipsis',
57464                                 'overflow' : 'hidden',
57465                                 'white-space' : 'nowrap',
57466                                 'cursor' : 'pointer'
57467                             },
57468                             '.sub-link' : {
57469                                 'color' : 'green'
57470                             },
57471                             '.de-act-sup-link' : {
57472                                 'color' : 'purple',
57473                                 'text-decoration' : 'line-through'
57474                             },
57475                             '.de-act-link' : {
57476                                 'color' : 'red',
57477                                 'text-decoration' : 'line-through'
57478                             },
57479                             '.course-timesheet .course-highlight' : {
57480                                 'border-top-style': 'dashed !important',
57481                                 'border-bottom-bottom': 'dashed !important'
57482                             },
57483                             '.course-timesheet .course-item' : {
57484                                 'font-family'   : 'tahoma, arial, helvetica',
57485                                 'font-size'     : '11px',
57486                                 'overflow'      : 'hidden',
57487                                 'padding-left'  : '10px',
57488                                 'padding-right' : '10px',
57489                                 'padding-top' : '10px' 
57490                             }
57491                             
57492                         }, Roo.id());
57493                                 this.ds.load({});
57494                     }
57495                 },
57496                 autoWidth : true,
57497                 monitorWindowResize : false,
57498                 cellrenderer : function(v,x,r)
57499                 {
57500                     return v;
57501                 },
57502                 sm : {
57503                     xtype: 'CellSelectionModel',
57504                     xns: Roo.grid
57505                 },
57506                 dataSource : {
57507                     xtype: 'Store',
57508                     xns: Roo.data,
57509                     listeners : {
57510                         beforeload : function (_self, options)
57511                         {
57512                             options.params = options.params || {};
57513                             options.params._month = _this.monthField.getValue();
57514                             options.params.limit = 9999;
57515                             options.params['sort'] = 'when_dt';    
57516                             options.params['dir'] = 'ASC';    
57517                             this.proxy.loadResponse = this.loadResponse;
57518                             Roo.log("load?");
57519                             //this.addColumns();
57520                         },
57521                         load : function (_self, records, options)
57522                         {
57523                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57524                                 // if you click on the translation.. you can edit it...
57525                                 var el = Roo.get(this);
57526                                 var id = el.dom.getAttribute('data-id');
57527                                 var d = el.dom.getAttribute('data-date');
57528                                 var t = el.dom.getAttribute('data-time');
57529                                 //var id = this.child('span').dom.textContent;
57530                                 
57531                                 //Roo.log(this);
57532                                 Pman.Dialog.CourseCalendar.show({
57533                                     id : id,
57534                                     when_d : d,
57535                                     when_t : t,
57536                                     productitem_active : id ? 1 : 0
57537                                 }, function() {
57538                                     _this.grid.ds.load({});
57539                                 });
57540                            
57541                            });
57542                            
57543                            _this.panel.fireEvent('resize', [ '', '' ]);
57544                         }
57545                     },
57546                     loadResponse : function(o, success, response){
57547                             // this is overridden on before load..
57548                             
57549                             Roo.log("our code?");       
57550                             //Roo.log(success);
57551                             //Roo.log(response)
57552                             delete this.activeRequest;
57553                             if(!success){
57554                                 this.fireEvent("loadexception", this, o, response);
57555                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57556                                 return;
57557                             }
57558                             var result;
57559                             try {
57560                                 result = o.reader.read(response);
57561                             }catch(e){
57562                                 Roo.log("load exception?");
57563                                 this.fireEvent("loadexception", this, o, response, e);
57564                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57565                                 return;
57566                             }
57567                             Roo.log("ready...");        
57568                             // loop through result.records;
57569                             // and set this.tdate[date] = [] << array of records..
57570                             _this.tdata  = {};
57571                             Roo.each(result.records, function(r){
57572                                 //Roo.log(r.data);
57573                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57574                                     _this.tdata[r.data.when_dt.format('j')] = [];
57575                                 }
57576                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57577                             });
57578                             
57579                             //Roo.log(_this.tdata);
57580                             
57581                             result.records = [];
57582                             result.totalRecords = 6;
57583                     
57584                             // let's generate some duumy records for the rows.
57585                             //var st = _this.dateField.getValue();
57586                             
57587                             // work out monday..
57588                             //st = st.add(Date.DAY, -1 * st.format('w'));
57589                             
57590                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57591                             
57592                             var firstOfMonth = date.getFirstDayOfMonth();
57593                             var days = date.getDaysInMonth();
57594                             var d = 1;
57595                             var firstAdded = false;
57596                             for (var i = 0; i < result.totalRecords ; i++) {
57597                                 //var d= st.add(Date.DAY, i);
57598                                 var row = {};
57599                                 var added = 0;
57600                                 for(var w = 0 ; w < 7 ; w++){
57601                                     if(!firstAdded && firstOfMonth != w){
57602                                         continue;
57603                                     }
57604                                     if(d > days){
57605                                         continue;
57606                                     }
57607                                     firstAdded = true;
57608                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57609                                     row['weekday'+w] = String.format(
57610                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57611                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57612                                                     d,
57613                                                     date.format('Y-m-')+dd
57614                                                 );
57615                                     added++;
57616                                     if(typeof(_this.tdata[d]) != 'undefined'){
57617                                         Roo.each(_this.tdata[d], function(r){
57618                                             var is_sub = '';
57619                                             var deactive = '';
57620                                             var id = r.id;
57621                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57622                                             if(r.parent_id*1>0){
57623                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57624                                                 id = r.parent_id;
57625                                             }
57626                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57627                                                 deactive = 'de-act-link';
57628                                             }
57629                                             
57630                                             row['weekday'+w] += String.format(
57631                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57632                                                     id, //0
57633                                                     r.product_id_name, //1
57634                                                     r.when_dt.format('h:ia'), //2
57635                                                     is_sub, //3
57636                                                     deactive, //4
57637                                                     desc // 5
57638                                             );
57639                                         });
57640                                     }
57641                                     d++;
57642                                 }
57643                                 
57644                                 // only do this if something added..
57645                                 if(added > 0){ 
57646                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57647                                 }
57648                                 
57649                                 
57650                                 // push it twice. (second one with an hour..
57651                                 
57652                             }
57653                             //Roo.log(result);
57654                             this.fireEvent("load", this, o, o.request.arg);
57655                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57656                         },
57657                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57658                     proxy : {
57659                         xtype: 'HttpProxy',
57660                         xns: Roo.data,
57661                         method : 'GET',
57662                         url : baseURL + '/Roo/Shop_course.php'
57663                     },
57664                     reader : {
57665                         xtype: 'JsonReader',
57666                         xns: Roo.data,
57667                         id : 'id',
57668                         fields : [
57669                             {
57670                                 'name': 'id',
57671                                 'type': 'int'
57672                             },
57673                             {
57674                                 'name': 'when_dt',
57675                                 'type': 'string'
57676                             },
57677                             {
57678                                 'name': 'end_dt',
57679                                 'type': 'string'
57680                             },
57681                             {
57682                                 'name': 'parent_id',
57683                                 'type': 'int'
57684                             },
57685                             {
57686                                 'name': 'product_id',
57687                                 'type': 'int'
57688                             },
57689                             {
57690                                 'name': 'productitem_id',
57691                                 'type': 'int'
57692                             },
57693                             {
57694                                 'name': 'guid',
57695                                 'type': 'int'
57696                             }
57697                         ]
57698                     }
57699                 },
57700                 toolbar : {
57701                     xtype: 'Toolbar',
57702                     xns: Roo,
57703                     items : [
57704                         {
57705                             xtype: 'Button',
57706                             xns: Roo.Toolbar,
57707                             listeners : {
57708                                 click : function (_self, e)
57709                                 {
57710                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57711                                     sd.setMonth(sd.getMonth()-1);
57712                                     _this.monthField.setValue(sd.format('Y-m-d'));
57713                                     _this.grid.ds.load({});
57714                                 }
57715                             },
57716                             text : "Back"
57717                         },
57718                         {
57719                             xtype: 'Separator',
57720                             xns: Roo.Toolbar
57721                         },
57722                         {
57723                             xtype: 'MonthField',
57724                             xns: Roo.form,
57725                             listeners : {
57726                                 render : function (_self)
57727                                 {
57728                                     _this.monthField = _self;
57729                                    // _this.monthField.set  today
57730                                 },
57731                                 select : function (combo, date)
57732                                 {
57733                                     _this.grid.ds.load({});
57734                                 }
57735                             },
57736                             value : (function() { return new Date(); })()
57737                         },
57738                         {
57739                             xtype: 'Separator',
57740                             xns: Roo.Toolbar
57741                         },
57742                         {
57743                             xtype: 'TextItem',
57744                             xns: Roo.Toolbar,
57745                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57746                         },
57747                         {
57748                             xtype: 'Fill',
57749                             xns: Roo.Toolbar
57750                         },
57751                         {
57752                             xtype: 'Button',
57753                             xns: Roo.Toolbar,
57754                             listeners : {
57755                                 click : function (_self, e)
57756                                 {
57757                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57758                                     sd.setMonth(sd.getMonth()+1);
57759                                     _this.monthField.setValue(sd.format('Y-m-d'));
57760                                     _this.grid.ds.load({});
57761                                 }
57762                             },
57763                             text : "Next"
57764                         }
57765                     ]
57766                 },
57767                  
57768             }
57769         };
57770         
57771         *//*
57772  * Based on:
57773  * Ext JS Library 1.1.1
57774  * Copyright(c) 2006-2007, Ext JS, LLC.
57775  *
57776  * Originally Released Under LGPL - original licence link has changed is not relivant.
57777  *
57778  * Fork - LGPL
57779  * <script type="text/javascript">
57780  */
57781  
57782 /**
57783  * @class Roo.LoadMask
57784  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57785  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57786  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57787  * element's UpdateManager load indicator and will be destroyed after the initial load.
57788  * @constructor
57789  * Create a new LoadMask
57790  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57791  * @param {Object} config The config object
57792  */
57793 Roo.LoadMask = function(el, config){
57794     this.el = Roo.get(el);
57795     Roo.apply(this, config);
57796     if(this.store){
57797         this.store.on('beforeload', this.onBeforeLoad, this);
57798         this.store.on('load', this.onLoad, this);
57799         this.store.on('loadexception', this.onLoadException, this);
57800         this.removeMask = false;
57801     }else{
57802         var um = this.el.getUpdateManager();
57803         um.showLoadIndicator = false; // disable the default indicator
57804         um.on('beforeupdate', this.onBeforeLoad, this);
57805         um.on('update', this.onLoad, this);
57806         um.on('failure', this.onLoad, this);
57807         this.removeMask = true;
57808     }
57809 };
57810
57811 Roo.LoadMask.prototype = {
57812     /**
57813      * @cfg {Boolean} removeMask
57814      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57815      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57816      */
57817     /**
57818      * @cfg {String} msg
57819      * The text to display in a centered loading message box (defaults to 'Loading...')
57820      */
57821     msg : 'Loading...',
57822     /**
57823      * @cfg {String} msgCls
57824      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57825      */
57826     msgCls : 'x-mask-loading',
57827
57828     /**
57829      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57830      * @type Boolean
57831      */
57832     disabled: false,
57833
57834     /**
57835      * Disables the mask to prevent it from being displayed
57836      */
57837     disable : function(){
57838        this.disabled = true;
57839     },
57840
57841     /**
57842      * Enables the mask so that it can be displayed
57843      */
57844     enable : function(){
57845         this.disabled = false;
57846     },
57847     
57848     onLoadException : function()
57849     {
57850         Roo.log(arguments);
57851         
57852         if (typeof(arguments[3]) != 'undefined') {
57853             Roo.MessageBox.alert("Error loading",arguments[3]);
57854         } 
57855         /*
57856         try {
57857             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57858                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57859             }   
57860         } catch(e) {
57861             
57862         }
57863         */
57864     
57865         
57866         
57867         this.el.unmask(this.removeMask);
57868     },
57869     // private
57870     onLoad : function()
57871     {
57872         this.el.unmask(this.removeMask);
57873     },
57874
57875     // private
57876     onBeforeLoad : function(){
57877         if(!this.disabled){
57878             this.el.mask(this.msg, this.msgCls);
57879         }
57880     },
57881
57882     // private
57883     destroy : function(){
57884         if(this.store){
57885             this.store.un('beforeload', this.onBeforeLoad, this);
57886             this.store.un('load', this.onLoad, this);
57887             this.store.un('loadexception', this.onLoadException, this);
57888         }else{
57889             var um = this.el.getUpdateManager();
57890             um.un('beforeupdate', this.onBeforeLoad, this);
57891             um.un('update', this.onLoad, this);
57892             um.un('failure', this.onLoad, this);
57893         }
57894     }
57895 };/*
57896  * Based on:
57897  * Ext JS Library 1.1.1
57898  * Copyright(c) 2006-2007, Ext JS, LLC.
57899  *
57900  * Originally Released Under LGPL - original licence link has changed is not relivant.
57901  *
57902  * Fork - LGPL
57903  * <script type="text/javascript">
57904  */
57905
57906
57907 /**
57908  * @class Roo.XTemplate
57909  * @extends Roo.Template
57910  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57911 <pre><code>
57912 var t = new Roo.XTemplate(
57913         '&lt;select name="{name}"&gt;',
57914                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57915         '&lt;/select&gt;'
57916 );
57917  
57918 // then append, applying the master template values
57919  </code></pre>
57920  *
57921  * Supported features:
57922  *
57923  *  Tags:
57924
57925 <pre><code>
57926       {a_variable} - output encoded.
57927       {a_variable.format:("Y-m-d")} - call a method on the variable
57928       {a_variable:raw} - unencoded output
57929       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57930       {a_variable:this.method_on_template(...)} - call a method on the template object.
57931  
57932 </code></pre>
57933  *  The tpl tag:
57934 <pre><code>
57935         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57936         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57937         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57938         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57939   
57940         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57941         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57942 </code></pre>
57943  *      
57944  */
57945 Roo.XTemplate = function()
57946 {
57947     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57948     if (this.html) {
57949         this.compile();
57950     }
57951 };
57952
57953
57954 Roo.extend(Roo.XTemplate, Roo.Template, {
57955
57956     /**
57957      * The various sub templates
57958      */
57959     tpls : false,
57960     /**
57961      *
57962      * basic tag replacing syntax
57963      * WORD:WORD()
57964      *
57965      * // you can fake an object call by doing this
57966      *  x.t:(test,tesT) 
57967      * 
57968      */
57969     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57970
57971     /**
57972      * compile the template
57973      *
57974      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57975      *
57976      */
57977     compile: function()
57978     {
57979         var s = this.html;
57980      
57981         s = ['<tpl>', s, '</tpl>'].join('');
57982     
57983         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57984             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57985             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57986             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57987             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57988             m,
57989             id     = 0,
57990             tpls   = [];
57991     
57992         while(true == !!(m = s.match(re))){
57993             var forMatch   = m[0].match(nameRe),
57994                 ifMatch   = m[0].match(ifRe),
57995                 execMatch   = m[0].match(execRe),
57996                 namedMatch   = m[0].match(namedRe),
57997                 
57998                 exp  = null, 
57999                 fn   = null,
58000                 exec = null,
58001                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58002                 
58003             if (ifMatch) {
58004                 // if - puts fn into test..
58005                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58006                 if(exp){
58007                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58008                 }
58009             }
58010             
58011             if (execMatch) {
58012                 // exec - calls a function... returns empty if true is  returned.
58013                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58014                 if(exp){
58015                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58016                 }
58017             }
58018             
58019             
58020             if (name) {
58021                 // for = 
58022                 switch(name){
58023                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58024                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58025                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58026                 }
58027             }
58028             var uid = namedMatch ? namedMatch[1] : id;
58029             
58030             
58031             tpls.push({
58032                 id:     namedMatch ? namedMatch[1] : id,
58033                 target: name,
58034                 exec:   exec,
58035                 test:   fn,
58036                 body:   m[1] || ''
58037             });
58038             if (namedMatch) {
58039                 s = s.replace(m[0], '');
58040             } else { 
58041                 s = s.replace(m[0], '{xtpl'+ id + '}');
58042             }
58043             ++id;
58044         }
58045         this.tpls = [];
58046         for(var i = tpls.length-1; i >= 0; --i){
58047             this.compileTpl(tpls[i]);
58048             this.tpls[tpls[i].id] = tpls[i];
58049         }
58050         this.master = tpls[tpls.length-1];
58051         return this;
58052     },
58053     /**
58054      * same as applyTemplate, except it's done to one of the subTemplates
58055      * when using named templates, you can do:
58056      *
58057      * var str = pl.applySubTemplate('your-name', values);
58058      *
58059      * 
58060      * @param {Number} id of the template
58061      * @param {Object} values to apply to template
58062      * @param {Object} parent (normaly the instance of this object)
58063      */
58064     applySubTemplate : function(id, values, parent)
58065     {
58066         
58067         
58068         var t = this.tpls[id];
58069         
58070         
58071         try { 
58072             if(t.test && !t.test.call(this, values, parent)){
58073                 return '';
58074             }
58075         } catch(e) {
58076             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58077             Roo.log(e.toString());
58078             Roo.log(t.test);
58079             return ''
58080         }
58081         try { 
58082             
58083             if(t.exec && t.exec.call(this, values, parent)){
58084                 return '';
58085             }
58086         } catch(e) {
58087             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58088             Roo.log(e.toString());
58089             Roo.log(t.exec);
58090             return ''
58091         }
58092         try {
58093             var vs = t.target ? t.target.call(this, values, parent) : values;
58094             parent = t.target ? values : parent;
58095             if(t.target && vs instanceof Array){
58096                 var buf = [];
58097                 for(var i = 0, len = vs.length; i < len; i++){
58098                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58099                 }
58100                 return buf.join('');
58101             }
58102             return t.compiled.call(this, vs, parent);
58103         } catch (e) {
58104             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58105             Roo.log(e.toString());
58106             Roo.log(t.compiled);
58107             return '';
58108         }
58109     },
58110
58111     compileTpl : function(tpl)
58112     {
58113         var fm = Roo.util.Format;
58114         var useF = this.disableFormats !== true;
58115         var sep = Roo.isGecko ? "+" : ",";
58116         var undef = function(str) {
58117             Roo.log("Property not found :"  + str);
58118             return '';
58119         };
58120         
58121         var fn = function(m, name, format, args)
58122         {
58123             //Roo.log(arguments);
58124             args = args ? args.replace(/\\'/g,"'") : args;
58125             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58126             if (typeof(format) == 'undefined') {
58127                 format= 'htmlEncode';
58128             }
58129             if (format == 'raw' ) {
58130                 format = false;
58131             }
58132             
58133             if(name.substr(0, 4) == 'xtpl'){
58134                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58135             }
58136             
58137             // build an array of options to determine if value is undefined..
58138             
58139             // basically get 'xxxx.yyyy' then do
58140             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58141             //    (function () { Roo.log("Property not found"); return ''; })() :
58142             //    ......
58143             
58144             var udef_ar = [];
58145             var lookfor = '';
58146             Roo.each(name.split('.'), function(st) {
58147                 lookfor += (lookfor.length ? '.': '') + st;
58148                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58149             });
58150             
58151             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58152             
58153             
58154             if(format && useF){
58155                 
58156                 args = args ? ',' + args : "";
58157                  
58158                 if(format.substr(0, 5) != "this."){
58159                     format = "fm." + format + '(';
58160                 }else{
58161                     format = 'this.call("'+ format.substr(5) + '", ';
58162                     args = ", values";
58163                 }
58164                 
58165                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58166             }
58167              
58168             if (args.length) {
58169                 // called with xxyx.yuu:(test,test)
58170                 // change to ()
58171                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58172             }
58173             // raw.. - :raw modifier..
58174             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58175             
58176         };
58177         var body;
58178         // branched to use + in gecko and [].join() in others
58179         if(Roo.isGecko){
58180             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58181                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58182                     "';};};";
58183         }else{
58184             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58185             body.push(tpl.body.replace(/(\r\n|\n)/g,
58186                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58187             body.push("'].join('');};};");
58188             body = body.join('');
58189         }
58190         
58191         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58192        
58193         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58194         eval(body);
58195         
58196         return this;
58197     },
58198
58199     applyTemplate : function(values){
58200         return this.master.compiled.call(this, values, {});
58201         //var s = this.subs;
58202     },
58203
58204     apply : function(){
58205         return this.applyTemplate.apply(this, arguments);
58206     }
58207
58208  });
58209
58210 Roo.XTemplate.from = function(el){
58211     el = Roo.getDom(el);
58212     return new Roo.XTemplate(el.value || el.innerHTML);
58213 };