roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4848 <p>
4849 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4850
4851 <p>
4852 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String} name The attribute name
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (!this.dom.hasAttribute(name)) {
9582                 return undefined;
9583             }
9584             return this.dom.getAttribute(name);
9585         }
9586         
9587         
9588         
9589     };
9590
9591     var ep = El.prototype;
9592
9593     /**
9594      * Appends an event handler (Shorthand for addListener)
9595      * @param {String}   eventName     The type of event to append
9596      * @param {Function} fn        The method the event invokes
9597      * @param {Object} scope       (optional) The scope (this object) of the fn
9598      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9599      * @method
9600      */
9601     ep.on = ep.addListener;
9602         // backwards compat
9603     ep.mon = ep.addListener;
9604
9605     /**
9606      * Removes an event handler from this element (shorthand for removeListener)
9607      * @param {String} eventName the type of event to remove
9608      * @param {Function} fn the method the event invokes
9609      * @return {Roo.Element} this
9610      * @method
9611      */
9612     ep.un = ep.removeListener;
9613
9614     /**
9615      * true to automatically adjust width and height settings for box-model issues (default to true)
9616      */
9617     ep.autoBoxAdjust = true;
9618
9619     // private
9620     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9621
9622     // private
9623     El.addUnits = function(v, defaultUnit){
9624         if(v === "" || v == "auto"){
9625             return v;
9626         }
9627         if(v === undefined){
9628             return '';
9629         }
9630         if(typeof v == "number" || !El.unitPattern.test(v)){
9631             return v + (defaultUnit || 'px');
9632         }
9633         return v;
9634     };
9635
9636     // special markup used throughout Roo when box wrapping elements
9637     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9638     /**
9639      * Visibility mode constant - Use visibility to hide element
9640      * @static
9641      * @type Number
9642      */
9643     El.VISIBILITY = 1;
9644     /**
9645      * Visibility mode constant - Use display to hide element
9646      * @static
9647      * @type Number
9648      */
9649     El.DISPLAY = 2;
9650
9651     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9652     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9653     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9654
9655
9656
9657     /**
9658      * @private
9659      */
9660     El.cache = {};
9661
9662     var docEl;
9663
9664     /**
9665      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9666      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9667      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9668      * @return {Element} The Element object
9669      * @static
9670      */
9671     El.get = function(el){
9672         var ex, elm, id;
9673         if(!el){ return null; }
9674         if(typeof el == "string"){ // element id
9675             if(!(elm = document.getElementById(el))){
9676                 return null;
9677             }
9678             if(ex = El.cache[el]){
9679                 ex.dom = elm;
9680             }else{
9681                 ex = El.cache[el] = new El(elm);
9682             }
9683             return ex;
9684         }else if(el.tagName){ // dom element
9685             if(!(id = el.id)){
9686                 id = Roo.id(el);
9687             }
9688             if(ex = El.cache[id]){
9689                 ex.dom = el;
9690             }else{
9691                 ex = El.cache[id] = new El(el);
9692             }
9693             return ex;
9694         }else if(el instanceof El){
9695             if(el != docEl){
9696                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9697                                                               // catch case where it hasn't been appended
9698                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9699             }
9700             return el;
9701         }else if(el.isComposite){
9702             return el;
9703         }else if(el instanceof Array){
9704             return El.select(el);
9705         }else if(el == document){
9706             // create a bogus element object representing the document object
9707             if(!docEl){
9708                 var f = function(){};
9709                 f.prototype = El.prototype;
9710                 docEl = new f();
9711                 docEl.dom = document;
9712             }
9713             return docEl;
9714         }
9715         return null;
9716     };
9717
9718     // private
9719     El.uncache = function(el){
9720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9721             if(a[i]){
9722                 delete El.cache[a[i].id || a[i]];
9723             }
9724         }
9725     };
9726
9727     // private
9728     // Garbage collection - uncache elements/purge listeners on orphaned elements
9729     // so we don't hold a reference and cause the browser to retain them
9730     El.garbageCollect = function(){
9731         if(!Roo.enableGarbageCollector){
9732             clearInterval(El.collectorThread);
9733             return;
9734         }
9735         for(var eid in El.cache){
9736             var el = El.cache[eid], d = el.dom;
9737             // -------------------------------------------------------
9738             // Determining what is garbage:
9739             // -------------------------------------------------------
9740             // !d
9741             // dom node is null, definitely garbage
9742             // -------------------------------------------------------
9743             // !d.parentNode
9744             // no parentNode == direct orphan, definitely garbage
9745             // -------------------------------------------------------
9746             // !d.offsetParent && !document.getElementById(eid)
9747             // display none elements have no offsetParent so we will
9748             // also try to look it up by it's id. However, check
9749             // offsetParent first so we don't do unneeded lookups.
9750             // This enables collection of elements that are not orphans
9751             // directly, but somewhere up the line they have an orphan
9752             // parent.
9753             // -------------------------------------------------------
9754             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9755                 delete El.cache[eid];
9756                 if(d && Roo.enableListenerCollection){
9757                     E.purgeElement(d);
9758                 }
9759             }
9760         }
9761     }
9762     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9763
9764
9765     // dom is optional
9766     El.Flyweight = function(dom){
9767         this.dom = dom;
9768     };
9769     El.Flyweight.prototype = El.prototype;
9770
9771     El._flyweights = {};
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * @param {String/HTMLElement} el The dom node or id
9776      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9777      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9778      * @static
9779      * @return {Element} The shared Element object
9780      */
9781     El.fly = function(el, named){
9782         named = named || '_global';
9783         el = Roo.getDom(el);
9784         if(!el){
9785             return null;
9786         }
9787         if(!El._flyweights[named]){
9788             El._flyweights[named] = new El.Flyweight();
9789         }
9790         El._flyweights[named].dom = el;
9791         return El._flyweights[named];
9792     };
9793
9794     /**
9795      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9796      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9797      * Shorthand of {@link Roo.Element#get}
9798      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9799      * @return {Element} The Element object
9800      * @member Roo
9801      * @method get
9802      */
9803     Roo.get = El.get;
9804     /**
9805      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9806      * the dom node can be overwritten by other code.
9807      * Shorthand of {@link Roo.Element#fly}
9808      * @param {String/HTMLElement} el The dom node or id
9809      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9810      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9811      * @static
9812      * @return {Element} The shared Element object
9813      * @member Roo
9814      * @method fly
9815      */
9816     Roo.fly = El.fly;
9817
9818     // speedy lookup for elements never to box adjust
9819     var noBoxAdjust = Roo.isStrict ? {
9820         select:1
9821     } : {
9822         input:1, select:1, textarea:1
9823     };
9824     if(Roo.isIE || Roo.isGecko){
9825         noBoxAdjust['button'] = 1;
9826     }
9827
9828
9829     Roo.EventManager.on(window, 'unload', function(){
9830         delete El.cache;
9831         delete El._flyweights;
9832     });
9833 })();
9834
9835
9836
9837
9838 if(Roo.DomQuery){
9839     Roo.Element.selectorFunction = Roo.DomQuery.select;
9840 }
9841
9842 Roo.Element.select = function(selector, unique, root){
9843     var els;
9844     if(typeof selector == "string"){
9845         els = Roo.Element.selectorFunction(selector, root);
9846     }else if(selector.length !== undefined){
9847         els = selector;
9848     }else{
9849         throw "Invalid selector";
9850     }
9851     if(unique === true){
9852         return new Roo.CompositeElement(els);
9853     }else{
9854         return new Roo.CompositeElementLite(els);
9855     }
9856 };
9857 /**
9858  * Selects elements based on the passed CSS selector to enable working on them as 1.
9859  * @param {String/Array} selector The CSS selector or an array of elements
9860  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9861  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9862  * @return {CompositeElementLite/CompositeElement}
9863  * @member Roo
9864  * @method select
9865  */
9866 Roo.select = Roo.Element.select;
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881 /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893
9894 //Notifies Element that fx methods are available
9895 Roo.enableFx = true;
9896
9897 /**
9898  * @class Roo.Fx
9899  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9900  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9901  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9902  * Element effects to work.</p><br/>
9903  *
9904  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9905  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9906  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9907  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9908  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9909  * expected results and should be done with care.</p><br/>
9910  *
9911  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9912  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9913 <pre>
9914 Value  Description
9915 -----  -----------------------------
9916 tl     The top left corner
9917 t      The center of the top edge
9918 tr     The top right corner
9919 l      The center of the left edge
9920 r      The center of the right edge
9921 bl     The bottom left corner
9922 b      The center of the bottom edge
9923 br     The bottom right corner
9924 </pre>
9925  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9926  * below are common options that can be passed to any Fx method.</b>
9927  * @cfg {Function} callback A function called when the effect is finished
9928  * @cfg {Object} scope The scope of the effect function
9929  * @cfg {String} easing A valid Easing value for the effect
9930  * @cfg {String} afterCls A css class to apply after the effect
9931  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9932  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9933  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9934  * effects that end with the element being visually hidden, ignored otherwise)
9935  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9936  * a function which returns such a specification that will be applied to the Element after the effect finishes
9937  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9938  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9939  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9940  */
9941 Roo.Fx = {
9942         /**
9943          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9944          * origin for the slide effect.  This function automatically handles wrapping the element with
9945          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element in from the top
9949 el.slideIn();
9950
9951 // custom: slide the element in from the right with a 2-second duration
9952 el.slideIn('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideIn('t', {
9956     easing: 'easeOut',
9957     duration: .5
9958 });
9959 </code></pre>
9960          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9961          * @param {Object} options (optional) Object literal with any of the Fx config options
9962          * @return {Roo.Element} The Element
9963          */
9964     slideIn : function(anchor, o){
9965         var el = this.getFxEl();
9966         o = o || {};
9967
9968         el.queueFx(o, function(){
9969
9970             anchor = anchor || "t";
9971
9972             // fix display to visibility
9973             this.fixDisplay();
9974
9975             // restore values after effect
9976             var r = this.getFxRestore();
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "hidden");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             // clear out temp styles after slide and unwrap
9989             var after = function(){
9990                 el.fxUnwrap(wrap, r.pos, o);
9991                 st.width = r.width;
9992                 st.height = r.height;
9993                 el.afterFx(o);
9994             };
9995             // time to calc the positions
9996             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9997
9998             switch(anchor.toLowerCase()){
9999                 case "t":
10000                     wrap.setSize(b.width, 0);
10001                     st.left = st.bottom = "0";
10002                     a = {height: bh};
10003                 break;
10004                 case "l":
10005                     wrap.setSize(0, b.height);
10006                     st.right = st.top = "0";
10007                     a = {width: bw};
10008                 break;
10009                 case "r":
10010                     wrap.setSize(0, b.height);
10011                     wrap.setX(b.right);
10012                     st.left = st.top = "0";
10013                     a = {width: bw, points: pt};
10014                 break;
10015                 case "b":
10016                     wrap.setSize(b.width, 0);
10017                     wrap.setY(b.bottom);
10018                     st.left = st.top = "0";
10019                     a = {height: bh, points: pt};
10020                 break;
10021                 case "tl":
10022                     wrap.setSize(0, 0);
10023                     st.right = st.bottom = "0";
10024                     a = {width: bw, height: bh};
10025                 break;
10026                 case "bl":
10027                     wrap.setSize(0, 0);
10028                     wrap.setY(b.y+b.height);
10029                     st.right = st.top = "0";
10030                     a = {width: bw, height: bh, points: pt};
10031                 break;
10032                 case "br":
10033                     wrap.setSize(0, 0);
10034                     wrap.setXY([b.right, b.bottom]);
10035                     st.left = st.top = "0";
10036                     a = {width: bw, height: bh, points: pt};
10037                 break;
10038                 case "tr":
10039                     wrap.setSize(0, 0);
10040                     wrap.setX(b.x+b.width);
10041                     st.left = st.bottom = "0";
10042                     a = {width: bw, height: bh, points: pt};
10043                 break;
10044             }
10045             this.dom.style.visibility = "visible";
10046             wrap.show();
10047
10048             arguments.callee.anim = wrap.fxanim(a,
10049                 o,
10050                 'motion',
10051                 .5,
10052                 'easeOut', after);
10053         });
10054         return this;
10055     },
10056     
10057         /**
10058          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10059          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10060          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10061          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10062          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10063          * Usage:
10064          *<pre><code>
10065 // default: slide the element out to the top
10066 el.slideOut();
10067
10068 // custom: slide the element out to the right with a 2-second duration
10069 el.slideOut('r', { duration: 2 });
10070
10071 // common config options shown with default values
10072 el.slideOut('t', {
10073     easing: 'easeOut',
10074     duration: .5,
10075     remove: false,
10076     useDisplay: false
10077 });
10078 </code></pre>
10079          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10080          * @param {Object} options (optional) Object literal with any of the Fx config options
10081          * @return {Roo.Element} The Element
10082          */
10083     slideOut : function(anchor, o){
10084         var el = this.getFxEl();
10085         o = o || {};
10086
10087         el.queueFx(o, function(){
10088
10089             anchor = anchor || "t";
10090
10091             // restore values after effect
10092             var r = this.getFxRestore();
10093             
10094             var b = this.getBox();
10095             // fixed size for slide
10096             this.setSize(b);
10097
10098             // wrap if needed
10099             var wrap = this.fxWrap(r.pos, o, "visible");
10100
10101             var st = this.dom.style;
10102             st.visibility = "visible";
10103             st.position = "absolute";
10104
10105             wrap.setSize(b);
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.fxUnwrap(wrap, r.pos, o);
10115
10116                 st.width = r.width;
10117                 st.height = r.height;
10118
10119                 el.afterFx(o);
10120             };
10121
10122             var a, zero = {to: 0};
10123             switch(anchor.toLowerCase()){
10124                 case "t":
10125                     st.left = st.bottom = "0";
10126                     a = {height: zero};
10127                 break;
10128                 case "l":
10129                     st.right = st.top = "0";
10130                     a = {width: zero};
10131                 break;
10132                 case "r":
10133                     st.left = st.top = "0";
10134                     a = {width: zero, points: {to:[b.right, b.y]}};
10135                 break;
10136                 case "b":
10137                     st.left = st.top = "0";
10138                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "tl":
10141                     st.right = st.bottom = "0";
10142                     a = {width: zero, height: zero};
10143                 break;
10144                 case "bl":
10145                     st.right = st.top = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "br":
10149                     st.left = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10151                 break;
10152                 case "tr":
10153                     st.left = st.bottom = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10155                 break;
10156             }
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 "easeOut", after);
10163         });
10164         return this;
10165     },
10166
10167         /**
10168          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10169          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10170          * The element must be removed from the DOM using the 'remove' config option if desired.
10171          * Usage:
10172          *<pre><code>
10173 // default
10174 el.puff();
10175
10176 // common config options shown with default values
10177 el.puff({
10178     easing: 'easeOut',
10179     duration: .5,
10180     remove: false,
10181     useDisplay: false
10182 });
10183 </code></pre>
10184          * @param {Object} options (optional) Object literal with any of the Fx config options
10185          * @return {Roo.Element} The Element
10186          */
10187     puff : function(o){
10188         var el = this.getFxEl();
10189         o = o || {};
10190
10191         el.queueFx(o, function(){
10192             this.clearOpacity();
10193             this.show();
10194
10195             // restore values after effect
10196             var r = this.getFxRestore();
10197             var st = this.dom.style;
10198
10199             var after = function(){
10200                 if(o.useDisplay){
10201                     el.setDisplayed(false);
10202                 }else{
10203                     el.hide();
10204                 }
10205
10206                 el.clearOpacity();
10207
10208                 el.setPositioning(r.pos);
10209                 st.width = r.width;
10210                 st.height = r.height;
10211                 st.fontSize = '';
10212                 el.afterFx(o);
10213             };
10214
10215             var width = this.getWidth();
10216             var height = this.getHeight();
10217
10218             arguments.callee.anim = this.fxanim({
10219                     width : {to: this.adjustWidth(width * 2)},
10220                     height : {to: this.adjustHeight(height * 2)},
10221                     points : {by: [-(width * .5), -(height * .5)]},
10222                     opacity : {to: 0},
10223                     fontSize: {to:200, unit: "%"}
10224                 },
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10235          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10236          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.switchOff();
10241
10242 // all config options shown with default values
10243 el.switchOff({
10244     easing: 'easeIn',
10245     duration: .3,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     switchOff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.clip();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273                 el.setPositioning(r.pos);
10274                 st.width = r.width;
10275                 st.height = r.height;
10276
10277                 el.afterFx(o);
10278             };
10279
10280             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10281                 this.clearOpacity();
10282                 (function(){
10283                     this.fxanim({
10284                         height:{to:1},
10285                         points:{by:[0, this.getHeight() * .5]}
10286                     }, o, 'motion', 0.3, 'easeIn', after);
10287                 }).defer(100, this);
10288             });
10289         });
10290         return this;
10291     },
10292
10293     /**
10294      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10295      * changed using the "attr" config option) and then fading back to the original color. If no original
10296      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10297      * Usage:
10298 <pre><code>
10299 // default: highlight background to yellow
10300 el.highlight();
10301
10302 // custom: highlight foreground text to blue for 2 seconds
10303 el.highlight("0000ff", { attr: 'color', duration: 2 });
10304
10305 // common config options shown with default values
10306 el.highlight("ffff9c", {
10307     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10308     endColor: (current color) or "ffffff",
10309     easing: 'easeIn',
10310     duration: 1
10311 });
10312 </code></pre>
10313      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10314      * @param {Object} options (optional) Object literal with any of the Fx config options
10315      * @return {Roo.Element} The Element
10316      */ 
10317     highlight : function(color, o){
10318         var el = this.getFxEl();
10319         o = o || {};
10320
10321         el.queueFx(o, function(){
10322             color = color || "ffff9c";
10323             attr = o.attr || "backgroundColor";
10324
10325             this.clearOpacity();
10326             this.show();
10327
10328             var origColor = this.getColor(attr);
10329             var restoreColor = this.dom.style[attr];
10330             endColor = (o.endColor || origColor) || "ffffff";
10331
10332             var after = function(){
10333                 el.dom.style[attr] = restoreColor;
10334                 el.afterFx(o);
10335             };
10336
10337             var a = {};
10338             a[attr] = {from: color, to: endColor};
10339             arguments.callee.anim = this.fxanim(a,
10340                 o,
10341                 'color',
10342                 1,
10343                 'easeIn', after);
10344         });
10345         return this;
10346     },
10347
10348    /**
10349     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10350     * Usage:
10351 <pre><code>
10352 // default: a single light blue ripple
10353 el.frame();
10354
10355 // custom: 3 red ripples lasting 3 seconds total
10356 el.frame("ff0000", 3, { duration: 3 });
10357
10358 // common config options shown with default values
10359 el.frame("C3DAF9", 1, {
10360     duration: 1 //duration of entire animation (not each individual ripple)
10361     // Note: Easing is not configurable and will be ignored if included
10362 });
10363 </code></pre>
10364     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10365     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10366     * @param {Object} options (optional) Object literal with any of the Fx config options
10367     * @return {Roo.Element} The Element
10368     */
10369     frame : function(color, count, o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             color = color || "#C3DAF9";
10375             if(color.length == 6){
10376                 color = "#" + color;
10377             }
10378             count = count || 1;
10379             duration = o.duration || 1;
10380             this.show();
10381
10382             var b = this.getBox();
10383             var animFn = function(){
10384                 var proxy = this.createProxy({
10385
10386                      style:{
10387                         visbility:"hidden",
10388                         position:"absolute",
10389                         "z-index":"35000", // yee haw
10390                         border:"0px solid " + color
10391                      }
10392                   });
10393                 var scale = Roo.isBorderBox ? 2 : 1;
10394                 proxy.animate({
10395                     top:{from:b.y, to:b.y - 20},
10396                     left:{from:b.x, to:b.x - 20},
10397                     borderWidth:{from:0, to:10},
10398                     opacity:{from:1, to:0},
10399                     height:{from:b.height, to:(b.height + (20*scale))},
10400                     width:{from:b.width, to:(b.width + (20*scale))}
10401                 }, duration, function(){
10402                     proxy.remove();
10403                 });
10404                 if(--count > 0){
10405                      animFn.defer((duration/2)*1000, this);
10406                 }else{
10407                     el.afterFx(o);
10408                 }
10409             };
10410             animFn.call(this);
10411         });
10412         return this;
10413     },
10414
10415    /**
10416     * Creates a pause before any subsequent queued effects begin.  If there are
10417     * no effects queued after the pause it will have no effect.
10418     * Usage:
10419 <pre><code>
10420 el.pause(1);
10421 </code></pre>
10422     * @param {Number} seconds The length of time to pause (in seconds)
10423     * @return {Roo.Element} The Element
10424     */
10425     pause : function(seconds){
10426         var el = this.getFxEl();
10427         var o = {};
10428
10429         el.queueFx(o, function(){
10430             setTimeout(function(){
10431                 el.afterFx(o);
10432             }, seconds * 1000);
10433         });
10434         return this;
10435     },
10436
10437    /**
10438     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10439     * using the "endOpacity" config option.
10440     * Usage:
10441 <pre><code>
10442 // default: fade in from opacity 0 to 100%
10443 el.fadeIn();
10444
10445 // custom: fade in from opacity 0 to 75% over 2 seconds
10446 el.fadeIn({ endOpacity: .75, duration: 2});
10447
10448 // common config options shown with default values
10449 el.fadeIn({
10450     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10451     easing: 'easeOut',
10452     duration: .5
10453 });
10454 </code></pre>
10455     * @param {Object} options (optional) Object literal with any of the Fx config options
10456     * @return {Roo.Element} The Element
10457     */
10458     fadeIn : function(o){
10459         var el = this.getFxEl();
10460         o = o || {};
10461         el.queueFx(o, function(){
10462             this.setOpacity(0);
10463             this.fixDisplay();
10464             this.dom.style.visibility = 'visible';
10465             var to = o.endOpacity || 1;
10466             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10467                 o, null, .5, "easeOut", function(){
10468                 if(to == 1){
10469                     this.clearOpacity();
10470                 }
10471                 el.afterFx(o);
10472             });
10473         });
10474         return this;
10475     },
10476
10477    /**
10478     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10479     * using the "endOpacity" config option.
10480     * Usage:
10481 <pre><code>
10482 // default: fade out from the element's current opacity to 0
10483 el.fadeOut();
10484
10485 // custom: fade out from the element's current opacity to 25% over 2 seconds
10486 el.fadeOut({ endOpacity: .25, duration: 2});
10487
10488 // common config options shown with default values
10489 el.fadeOut({
10490     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10491     easing: 'easeOut',
10492     duration: .5
10493     remove: false,
10494     useDisplay: false
10495 });
10496 </code></pre>
10497     * @param {Object} options (optional) Object literal with any of the Fx config options
10498     * @return {Roo.Element} The Element
10499     */
10500     fadeOut : function(o){
10501         var el = this.getFxEl();
10502         o = o || {};
10503         el.queueFx(o, function(){
10504             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10505                 o, null, .5, "easeOut", function(){
10506                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10507                      this.dom.style.display = "none";
10508                 }else{
10509                      this.dom.style.visibility = "hidden";
10510                 }
10511                 this.clearOpacity();
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Animates the transition of an element's dimensions from a starting height/width
10520     * to an ending height/width.
10521     * Usage:
10522 <pre><code>
10523 // change height and width to 100x100 pixels
10524 el.scale(100, 100);
10525
10526 // common config options shown with default values.  The height and width will default to
10527 // the element's existing values if passed as null.
10528 el.scale(
10529     [element's width],
10530     [element's height], {
10531     easing: 'easeOut',
10532     duration: .35
10533 });
10534 </code></pre>
10535     * @param {Number} width  The new width (pass undefined to keep the original width)
10536     * @param {Number} height  The new height (pass undefined to keep the original height)
10537     * @param {Object} options (optional) Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     scale : function(w, h, o){
10541         this.shift(Roo.apply({}, o, {
10542             width: w,
10543             height: h
10544         }));
10545         return this;
10546     },
10547
10548    /**
10549     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10550     * Any of these properties not specified in the config object will not be changed.  This effect 
10551     * requires that at least one new dimension, position or opacity setting must be passed in on
10552     * the config object in order for the function to have any effect.
10553     * Usage:
10554 <pre><code>
10555 // slide the element horizontally to x position 200 while changing the height and opacity
10556 el.shift({ x: 200, height: 50, opacity: .8 });
10557
10558 // common config options shown with default values.
10559 el.shift({
10560     width: [element's width],
10561     height: [element's height],
10562     x: [element's x position],
10563     y: [element's y position],
10564     opacity: [element's opacity],
10565     easing: 'easeOut',
10566     duration: .35
10567 });
10568 </code></pre>
10569     * @param {Object} options  Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     shift : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10577             if(w !== undefined){
10578                 a.width = {to: this.adjustWidth(w)};
10579             }
10580             if(h !== undefined){
10581                 a.height = {to: this.adjustHeight(h)};
10582             }
10583             if(x !== undefined || y !== undefined){
10584                 a.points = {to: [
10585                     x !== undefined ? x : this.getX(),
10586                     y !== undefined ? y : this.getY()
10587                 ]};
10588             }
10589             if(op !== undefined){
10590                 a.opacity = {to: op};
10591             }
10592             if(o.xy !== undefined){
10593                 a.points = {to: o.xy};
10594             }
10595             arguments.callee.anim = this.fxanim(a,
10596                 o, 'motion', .35, "easeOut", function(){
10597                 el.afterFx(o);
10598             });
10599         });
10600         return this;
10601     },
10602
10603         /**
10604          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10605          * ending point of the effect.
10606          * Usage:
10607          *<pre><code>
10608 // default: slide the element downward while fading out
10609 el.ghost();
10610
10611 // custom: slide the element out to the right with a 2-second duration
10612 el.ghost('r', { duration: 2 });
10613
10614 // common config options shown with default values
10615 el.ghost('b', {
10616     easing: 'easeOut',
10617     duration: .5
10618     remove: false,
10619     useDisplay: false
10620 });
10621 </code></pre>
10622          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10623          * @param {Object} options (optional) Object literal with any of the Fx config options
10624          * @return {Roo.Element} The Element
10625          */
10626     ghost : function(anchor, o){
10627         var el = this.getFxEl();
10628         o = o || {};
10629
10630         el.queueFx(o, function(){
10631             anchor = anchor || "b";
10632
10633             // restore values after effect
10634             var r = this.getFxRestore();
10635             var w = this.getWidth(),
10636                 h = this.getHeight();
10637
10638             var st = this.dom.style;
10639
10640             var after = function(){
10641                 if(o.useDisplay){
10642                     el.setDisplayed(false);
10643                 }else{
10644                     el.hide();
10645                 }
10646
10647                 el.clearOpacity();
10648                 el.setPositioning(r.pos);
10649                 st.width = r.width;
10650                 st.height = r.height;
10651
10652                 el.afterFx(o);
10653             };
10654
10655             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10656             switch(anchor.toLowerCase()){
10657                 case "t":
10658                     pt.by = [0, -h];
10659                 break;
10660                 case "l":
10661                     pt.by = [-w, 0];
10662                 break;
10663                 case "r":
10664                     pt.by = [w, 0];
10665                 break;
10666                 case "b":
10667                     pt.by = [0, h];
10668                 break;
10669                 case "tl":
10670                     pt.by = [-w, -h];
10671                 break;
10672                 case "bl":
10673                     pt.by = [-w, h];
10674                 break;
10675                 case "br":
10676                     pt.by = [w, h];
10677                 break;
10678                 case "tr":
10679                     pt.by = [w, -h];
10680                 break;
10681             }
10682
10683             arguments.callee.anim = this.fxanim(a,
10684                 o,
10685                 'motion',
10686                 .5,
10687                 "easeOut", after);
10688         });
10689         return this;
10690     },
10691
10692         /**
10693          * Ensures that all effects queued after syncFx is called on the element are
10694          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10695          * @return {Roo.Element} The Element
10696          */
10697     syncFx : function(){
10698         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10699             block : false,
10700             concurrent : true,
10701             stopFx : false
10702         });
10703         return this;
10704     },
10705
10706         /**
10707          * Ensures that all effects queued after sequenceFx is called on the element are
10708          * run in sequence.  This is the opposite of {@link #syncFx}.
10709          * @return {Roo.Element} The Element
10710          */
10711     sequenceFx : function(){
10712         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10713             block : false,
10714             concurrent : false,
10715             stopFx : false
10716         });
10717         return this;
10718     },
10719
10720         /* @private */
10721     nextFx : function(){
10722         var ef = this.fxQueue[0];
10723         if(ef){
10724             ef.call(this);
10725         }
10726     },
10727
10728         /**
10729          * Returns true if the element has any effects actively running or queued, else returns false.
10730          * @return {Boolean} True if element has active effects, else false
10731          */
10732     hasActiveFx : function(){
10733         return this.fxQueue && this.fxQueue[0];
10734     },
10735
10736         /**
10737          * Stops any running effects and clears the element's internal effects queue if it contains
10738          * any additional effects that haven't started yet.
10739          * @return {Roo.Element} The Element
10740          */
10741     stopFx : function(){
10742         if(this.hasActiveFx()){
10743             var cur = this.fxQueue[0];
10744             if(cur && cur.anim && cur.anim.isAnimated()){
10745                 this.fxQueue = [cur]; // clear out others
10746                 cur.anim.stop(true);
10747             }
10748         }
10749         return this;
10750     },
10751
10752         /* @private */
10753     beforeFx : function(o){
10754         if(this.hasActiveFx() && !o.concurrent){
10755            if(o.stopFx){
10756                this.stopFx();
10757                return true;
10758            }
10759            return false;
10760         }
10761         return true;
10762     },
10763
10764         /**
10765          * Returns true if the element is currently blocking so that no other effect can be queued
10766          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10767          * used to ensure that an effect initiated by a user action runs to completion prior to the
10768          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10769          * @return {Boolean} True if blocking, else false
10770          */
10771     hasFxBlock : function(){
10772         var q = this.fxQueue;
10773         return q && q[0] && q[0].block;
10774     },
10775
10776         /* @private */
10777     queueFx : function(o, fn){
10778         if(!this.fxQueue){
10779             this.fxQueue = [];
10780         }
10781         if(!this.hasFxBlock()){
10782             Roo.applyIf(o, this.fxDefaults);
10783             if(!o.concurrent){
10784                 var run = this.beforeFx(o);
10785                 fn.block = o.block;
10786                 this.fxQueue.push(fn);
10787                 if(run){
10788                     this.nextFx();
10789                 }
10790             }else{
10791                 fn.call(this);
10792             }
10793         }
10794         return this;
10795     },
10796
10797         /* @private */
10798     fxWrap : function(pos, o, vis){
10799         var wrap;
10800         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10801             var wrapXY;
10802             if(o.fixPosition){
10803                 wrapXY = this.getXY();
10804             }
10805             var div = document.createElement("div");
10806             div.style.visibility = vis;
10807             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10808             wrap.setPositioning(pos);
10809             if(wrap.getStyle("position") == "static"){
10810                 wrap.position("relative");
10811             }
10812             this.clearPositioning('auto');
10813             wrap.clip();
10814             wrap.dom.appendChild(this.dom);
10815             if(wrapXY){
10816                 wrap.setXY(wrapXY);
10817             }
10818         }
10819         return wrap;
10820     },
10821
10822         /* @private */
10823     fxUnwrap : function(wrap, pos, o){
10824         this.clearPositioning();
10825         this.setPositioning(pos);
10826         if(!o.wrap){
10827             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10828             wrap.remove();
10829         }
10830     },
10831
10832         /* @private */
10833     getFxRestore : function(){
10834         var st = this.dom.style;
10835         return {pos: this.getPositioning(), width: st.width, height : st.height};
10836     },
10837
10838         /* @private */
10839     afterFx : function(o){
10840         if(o.afterStyle){
10841             this.applyStyles(o.afterStyle);
10842         }
10843         if(o.afterCls){
10844             this.addClass(o.afterCls);
10845         }
10846         if(o.remove === true){
10847             this.remove();
10848         }
10849         Roo.callback(o.callback, o.scope, [this]);
10850         if(!o.concurrent){
10851             this.fxQueue.shift();
10852             this.nextFx();
10853         }
10854     },
10855
10856         /* @private */
10857     getFxEl : function(){ // support for composite element fx
10858         return Roo.get(this.dom);
10859     },
10860
10861         /* @private */
10862     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10863         animType = animType || 'run';
10864         opt = opt || {};
10865         var anim = Roo.lib.Anim[animType](
10866             this.dom, args,
10867             (opt.duration || defaultDur) || .35,
10868             (opt.easing || defaultEase) || 'easeOut',
10869             function(){
10870                 Roo.callback(cb, this);
10871             },
10872             this
10873         );
10874         opt.anim = anim;
10875         return anim;
10876     }
10877 };
10878
10879 // backwords compat
10880 Roo.Fx.resize = Roo.Fx.scale;
10881
10882 //When included, Roo.Fx is automatically applied to Element so that all basic
10883 //effects are available directly via the Element API
10884 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10885  * Based on:
10886  * Ext JS Library 1.1.1
10887  * Copyright(c) 2006-2007, Ext JS, LLC.
10888  *
10889  * Originally Released Under LGPL - original licence link has changed is not relivant.
10890  *
10891  * Fork - LGPL
10892  * <script type="text/javascript">
10893  */
10894
10895
10896 /**
10897  * @class Roo.CompositeElement
10898  * Standard composite class. Creates a Roo.Element for every element in the collection.
10899  * <br><br>
10900  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10901  * actions will be performed on all the elements in this collection.</b>
10902  * <br><br>
10903  * All methods return <i>this</i> and can be chained.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class", true);
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class', true);
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre>
10915  */
10916 Roo.CompositeElement = function(els){
10917     this.elements = [];
10918     this.addElements(els);
10919 };
10920 Roo.CompositeElement.prototype = {
10921     isComposite: true,
10922     addElements : function(els){
10923         if(!els) return this;
10924         if(typeof els == "string"){
10925             els = Roo.Element.selectorFunction(els);
10926         }
10927         var yels = this.elements;
10928         var index = yels.length-1;
10929         for(var i = 0, len = els.length; i < len; i++) {
10930                 yels[++index] = Roo.get(els[i]);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Clears this composite and adds the elements returned by the passed selector.
10937     * @param {String/Array} els A string CSS selector, an array of elements or an element
10938     * @return {CompositeElement} this
10939     */
10940     fill : function(els){
10941         this.elements = [];
10942         this.add(els);
10943         return this;
10944     },
10945
10946     /**
10947     * Filters this composite to only elements that match the passed selector.
10948     * @param {String} selector A string CSS selector
10949     * @return {CompositeElement} this
10950     */
10951     filter : function(selector){
10952         var els = [];
10953         this.each(function(el){
10954             if(el.is(selector)){
10955                 els[els.length] = el.dom;
10956             }
10957         });
10958         this.fill(els);
10959         return this;
10960     },
10961
10962     invoke : function(fn, args){
10963         var els = this.elements;
10964         for(var i = 0, len = els.length; i < len; i++) {
10965                 Roo.Element.prototype[fn].apply(els[i], args);
10966         }
10967         return this;
10968     },
10969     /**
10970     * Adds elements to this composite.
10971     * @param {String/Array} els A string CSS selector, an array of elements or an element
10972     * @return {CompositeElement} this
10973     */
10974     add : function(els){
10975         if(typeof els == "string"){
10976             this.addElements(Roo.Element.selectorFunction(els));
10977         }else if(els.length !== undefined){
10978             this.addElements(els);
10979         }else{
10980             this.addElements([els]);
10981         }
10982         return this;
10983     },
10984     /**
10985     * Calls the passed function passing (el, this, index) for each element in this composite.
10986     * @param {Function} fn The function to call
10987     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10988     * @return {CompositeElement} this
10989     */
10990     each : function(fn, scope){
10991         var els = this.elements;
10992         for(var i = 0, len = els.length; i < len; i++){
10993             if(fn.call(scope || els[i], els[i], this, i) === false) {
10994                 break;
10995             }
10996         }
10997         return this;
10998     },
10999
11000     /**
11001      * Returns the Element object at the specified index
11002      * @param {Number} index
11003      * @return {Roo.Element}
11004      */
11005     item : function(index){
11006         return this.elements[index] || null;
11007     },
11008
11009     /**
11010      * Returns the first Element
11011      * @return {Roo.Element}
11012      */
11013     first : function(){
11014         return this.item(0);
11015     },
11016
11017     /**
11018      * Returns the last Element
11019      * @return {Roo.Element}
11020      */
11021     last : function(){
11022         return this.item(this.elements.length-1);
11023     },
11024
11025     /**
11026      * Returns the number of elements in this composite
11027      * @return Number
11028      */
11029     getCount : function(){
11030         return this.elements.length;
11031     },
11032
11033     /**
11034      * Returns true if this composite contains the passed element
11035      * @return Boolean
11036      */
11037     contains : function(el){
11038         return this.indexOf(el) !== -1;
11039     },
11040
11041     /**
11042      * Returns true if this composite contains the passed element
11043      * @return Boolean
11044      */
11045     indexOf : function(el){
11046         return this.elements.indexOf(Roo.get(el));
11047     },
11048
11049
11050     /**
11051     * Removes the specified element(s).
11052     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11053     * or an array of any of those.
11054     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11055     * @return {CompositeElement} this
11056     */
11057     removeElement : function(el, removeDom){
11058         if(el instanceof Array){
11059             for(var i = 0, len = el.length; i < len; i++){
11060                 this.removeElement(el[i]);
11061             }
11062             return this;
11063         }
11064         var index = typeof el == 'number' ? el : this.indexOf(el);
11065         if(index !== -1){
11066             if(removeDom){
11067                 var d = this.elements[index];
11068                 if(d.dom){
11069                     d.remove();
11070                 }else{
11071                     d.parentNode.removeChild(d);
11072                 }
11073             }
11074             this.elements.splice(index, 1);
11075         }
11076         return this;
11077     },
11078
11079     /**
11080     * Replaces the specified element with the passed element.
11081     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11082     * to replace.
11083     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11084     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11085     * @return {CompositeElement} this
11086     */
11087     replaceElement : function(el, replacement, domReplace){
11088         var index = typeof el == 'number' ? el : this.indexOf(el);
11089         if(index !== -1){
11090             if(domReplace){
11091                 this.elements[index].replaceWith(replacement);
11092             }else{
11093                 this.elements.splice(index, 1, Roo.get(replacement))
11094             }
11095         }
11096         return this;
11097     },
11098
11099     /**
11100      * Removes all elements.
11101      */
11102     clear : function(){
11103         this.elements = [];
11104     }
11105 };
11106 (function(){
11107     Roo.CompositeElement.createCall = function(proto, fnName){
11108         if(!proto[fnName]){
11109             proto[fnName] = function(){
11110                 return this.invoke(fnName, arguments);
11111             };
11112         }
11113     };
11114     for(var fnName in Roo.Element.prototype){
11115         if(typeof Roo.Element.prototype[fnName] == "function"){
11116             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11117         }
11118     };
11119 })();
11120 /*
11121  * Based on:
11122  * Ext JS Library 1.1.1
11123  * Copyright(c) 2006-2007, Ext JS, LLC.
11124  *
11125  * Originally Released Under LGPL - original licence link has changed is not relivant.
11126  *
11127  * Fork - LGPL
11128  * <script type="text/javascript">
11129  */
11130
11131 /**
11132  * @class Roo.CompositeElementLite
11133  * @extends Roo.CompositeElement
11134  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11135  <pre><code>
11136  var els = Roo.select("#some-el div.some-class");
11137  // or select directly from an existing element
11138  var el = Roo.get('some-el');
11139  el.select('div.some-class');
11140
11141  els.setWidth(100); // all elements become 100 width
11142  els.hide(true); // all elements fade out and hide
11143  // or
11144  els.setWidth(100).hide(true);
11145  </code></pre><br><br>
11146  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11147  * actions will be performed on all the elements in this collection.</b>
11148  */
11149 Roo.CompositeElementLite = function(els){
11150     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11151     this.el = new Roo.Element.Flyweight();
11152 };
11153 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11154     addElements : function(els){
11155         if(els){
11156             if(els instanceof Array){
11157                 this.elements = this.elements.concat(els);
11158             }else{
11159                 var yels = this.elements;
11160                 var index = yels.length-1;
11161                 for(var i = 0, len = els.length; i < len; i++) {
11162                     yels[++index] = els[i];
11163                 }
11164             }
11165         }
11166         return this;
11167     },
11168     invoke : function(fn, args){
11169         var els = this.elements;
11170         var el = this.el;
11171         for(var i = 0, len = els.length; i < len; i++) {
11172             el.dom = els[i];
11173                 Roo.Element.prototype[fn].apply(el, args);
11174         }
11175         return this;
11176     },
11177     /**
11178      * Returns a flyweight Element of the dom element object at the specified index
11179      * @param {Number} index
11180      * @return {Roo.Element}
11181      */
11182     item : function(index){
11183         if(!this.elements[index]){
11184             return null;
11185         }
11186         this.el.dom = this.elements[index];
11187         return this.el;
11188     },
11189
11190     // fixes scope with flyweight
11191     addListener : function(eventName, handler, scope, opt){
11192         var els = this.elements;
11193         for(var i = 0, len = els.length; i < len; i++) {
11194             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11195         }
11196         return this;
11197     },
11198
11199     /**
11200     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11201     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11202     * a reference to the dom node, use el.dom.</b>
11203     * @param {Function} fn The function to call
11204     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11205     * @return {CompositeElement} this
11206     */
11207     each : function(fn, scope){
11208         var els = this.elements;
11209         var el = this.el;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             el.dom = els[i];
11212                 if(fn.call(scope || el, el, this, i) === false){
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     indexOf : function(el){
11220         return this.elements.indexOf(Roo.getDom(el));
11221     },
11222
11223     replaceElement : function(el, replacement, domReplace){
11224         var index = typeof el == 'number' ? el : this.indexOf(el);
11225         if(index !== -1){
11226             replacement = Roo.getDom(replacement);
11227             if(domReplace){
11228                 var d = this.elements[index];
11229                 d.parentNode.insertBefore(replacement, d);
11230                 d.parentNode.removeChild(d);
11231             }
11232             this.elements.splice(index, 1, replacement);
11233         }
11234         return this;
11235     }
11236 });
11237 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11238
11239 /*
11240  * Based on:
11241  * Ext JS Library 1.1.1
11242  * Copyright(c) 2006-2007, Ext JS, LLC.
11243  *
11244  * Originally Released Under LGPL - original licence link has changed is not relivant.
11245  *
11246  * Fork - LGPL
11247  * <script type="text/javascript">
11248  */
11249
11250  
11251
11252 /**
11253  * @class Roo.data.Connection
11254  * @extends Roo.util.Observable
11255  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11256  * either to a configured URL, or to a URL specified at request time.<br><br>
11257  * <p>
11258  * Requests made by this class are asynchronous, and will return immediately. No data from
11259  * the server will be available to the statement immediately following the {@link #request} call.
11260  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11261  * <p>
11262  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11263  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11264  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11265  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11266  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11267  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11268  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11269  * standard DOM methods.
11270  * @constructor
11271  * @param {Object} config a configuration object.
11272  */
11273 Roo.data.Connection = function(config){
11274     Roo.apply(this, config);
11275     this.addEvents({
11276         /**
11277          * @event beforerequest
11278          * Fires before a network request is made to retrieve a data object.
11279          * @param {Connection} conn This Connection object.
11280          * @param {Object} options The options config object passed to the {@link #request} method.
11281          */
11282         "beforerequest" : true,
11283         /**
11284          * @event requestcomplete
11285          * Fires if the request was successfully completed.
11286          * @param {Connection} conn This Connection object.
11287          * @param {Object} response The XHR object containing the response data.
11288          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11289          * @param {Object} options The options config object passed to the {@link #request} method.
11290          */
11291         "requestcomplete" : true,
11292         /**
11293          * @event requestexception
11294          * Fires if an error HTTP status was returned from the server.
11295          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11296          * @param {Connection} conn This Connection object.
11297          * @param {Object} response The XHR object containing the response data.
11298          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11299          * @param {Object} options The options config object passed to the {@link #request} method.
11300          */
11301         "requestexception" : true
11302     });
11303     Roo.data.Connection.superclass.constructor.call(this);
11304 };
11305
11306 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11307     /**
11308      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11309      */
11310     /**
11311      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11312      * extra parameters to each request made by this object. (defaults to undefined)
11313      */
11314     /**
11315      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11316      *  to each request made by this object. (defaults to undefined)
11317      */
11318     /**
11319      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11320      */
11321     /**
11322      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11323      */
11324     timeout : 30000,
11325     /**
11326      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11327      * @type Boolean
11328      */
11329     autoAbort:false,
11330
11331     /**
11332      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11333      * @type Boolean
11334      */
11335     disableCaching: true,
11336
11337     /**
11338      * Sends an HTTP request to a remote server.
11339      * @param {Object} options An object which may contain the following properties:<ul>
11340      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11341      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11342      * request, a url encoded string or a function to call to get either.</li>
11343      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11344      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11345      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11346      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11347      * <li>options {Object} The parameter to the request call.</li>
11348      * <li>success {Boolean} True if the request succeeded.</li>
11349      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11350      * </ul></li>
11351      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11352      * The callback is passed the following parameters:<ul>
11353      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11354      * <li>options {Object} The parameter to the request call.</li>
11355      * </ul></li>
11356      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11357      * The callback is passed the following parameters:<ul>
11358      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11359      * <li>options {Object} The parameter to the request call.</li>
11360      * </ul></li>
11361      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11362      * for the callback function. Defaults to the browser window.</li>
11363      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11364      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11365      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11366      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11367      * params for the post data. Any params will be appended to the URL.</li>
11368      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11369      * </ul>
11370      * @return {Number} transactionId
11371      */
11372     request : function(o){
11373         if(this.fireEvent("beforerequest", this, o) !== false){
11374             var p = o.params;
11375
11376             if(typeof p == "function"){
11377                 p = p.call(o.scope||window, o);
11378             }
11379             if(typeof p == "object"){
11380                 p = Roo.urlEncode(o.params);
11381             }
11382             if(this.extraParams){
11383                 var extras = Roo.urlEncode(this.extraParams);
11384                 p = p ? (p + '&' + extras) : extras;
11385             }
11386
11387             var url = o.url || this.url;
11388             if(typeof url == 'function'){
11389                 url = url.call(o.scope||window, o);
11390             }
11391
11392             if(o.form){
11393                 var form = Roo.getDom(o.form);
11394                 url = url || form.action;
11395
11396                 var enctype = form.getAttribute("enctype");
11397                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11398                     return this.doFormUpload(o, p, url);
11399                 }
11400                 var f = Roo.lib.Ajax.serializeForm(form);
11401                 p = p ? (p + '&' + f) : f;
11402             }
11403
11404             var hs = o.headers;
11405             if(this.defaultHeaders){
11406                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11407                 if(!o.headers){
11408                     o.headers = hs;
11409                 }
11410             }
11411
11412             var cb = {
11413                 success: this.handleResponse,
11414                 failure: this.handleFailure,
11415                 scope: this,
11416                 argument: {options: o},
11417                 timeout : o.timeout || this.timeout
11418             };
11419
11420             var method = o.method||this.method||(p ? "POST" : "GET");
11421
11422             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11423                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11424             }
11425
11426             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11427                 if(o.autoAbort){
11428                     this.abort();
11429                 }
11430             }else if(this.autoAbort !== false){
11431                 this.abort();
11432             }
11433
11434             if((method == 'GET' && p) || o.xmlData){
11435                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11436                 p = '';
11437             }
11438             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11439             return this.transId;
11440         }else{
11441             Roo.callback(o.callback, o.scope, [o, null, null]);
11442             return null;
11443         }
11444     },
11445
11446     /**
11447      * Determine whether this object has a request outstanding.
11448      * @param {Number} transactionId (Optional) defaults to the last transaction
11449      * @return {Boolean} True if there is an outstanding request.
11450      */
11451     isLoading : function(transId){
11452         if(transId){
11453             return Roo.lib.Ajax.isCallInProgress(transId);
11454         }else{
11455             return this.transId ? true : false;
11456         }
11457     },
11458
11459     /**
11460      * Aborts any outstanding request.
11461      * @param {Number} transactionId (Optional) defaults to the last transaction
11462      */
11463     abort : function(transId){
11464         if(transId || this.isLoading()){
11465             Roo.lib.Ajax.abort(transId || this.transId);
11466         }
11467     },
11468
11469     // private
11470     handleResponse : function(response){
11471         this.transId = false;
11472         var options = response.argument.options;
11473         response.argument = options ? options.argument : null;
11474         this.fireEvent("requestcomplete", this, response, options);
11475         Roo.callback(options.success, options.scope, [response, options]);
11476         Roo.callback(options.callback, options.scope, [options, true, response]);
11477     },
11478
11479     // private
11480     handleFailure : function(response, e){
11481         this.transId = false;
11482         var options = response.argument.options;
11483         response.argument = options ? options.argument : null;
11484         this.fireEvent("requestexception", this, response, options, e);
11485         Roo.callback(options.failure, options.scope, [response, options]);
11486         Roo.callback(options.callback, options.scope, [options, false, response]);
11487     },
11488
11489     // private
11490     doFormUpload : function(o, ps, url){
11491         var id = Roo.id();
11492         var frame = document.createElement('iframe');
11493         frame.id = id;
11494         frame.name = id;
11495         frame.className = 'x-hidden';
11496         if(Roo.isIE){
11497             frame.src = Roo.SSL_SECURE_URL;
11498         }
11499         document.body.appendChild(frame);
11500
11501         if(Roo.isIE){
11502            document.frames[id].name = id;
11503         }
11504
11505         var form = Roo.getDom(o.form);
11506         form.target = id;
11507         form.method = 'POST';
11508         form.enctype = form.encoding = 'multipart/form-data';
11509         if(url){
11510             form.action = url;
11511         }
11512
11513         var hiddens, hd;
11514         if(ps){ // add dynamic params
11515             hiddens = [];
11516             ps = Roo.urlDecode(ps, false);
11517             for(var k in ps){
11518                 if(ps.hasOwnProperty(k)){
11519                     hd = document.createElement('input');
11520                     hd.type = 'hidden';
11521                     hd.name = k;
11522                     hd.value = ps[k];
11523                     form.appendChild(hd);
11524                     hiddens.push(hd);
11525                 }
11526             }
11527         }
11528
11529         function cb(){
11530             var r = {  // bogus response object
11531                 responseText : '',
11532                 responseXML : null
11533             };
11534
11535             r.argument = o ? o.argument : null;
11536
11537             try { //
11538                 var doc;
11539                 if(Roo.isIE){
11540                     doc = frame.contentWindow.document;
11541                 }else {
11542                     doc = (frame.contentDocument || window.frames[id].document);
11543                 }
11544                 if(doc && doc.body){
11545                     r.responseText = doc.body.innerHTML;
11546                 }
11547                 if(doc && doc.XMLDocument){
11548                     r.responseXML = doc.XMLDocument;
11549                 }else {
11550                     r.responseXML = doc;
11551                 }
11552             }
11553             catch(e) {
11554                 // ignore
11555             }
11556
11557             Roo.EventManager.removeListener(frame, 'load', cb, this);
11558
11559             this.fireEvent("requestcomplete", this, r, o);
11560             Roo.callback(o.success, o.scope, [r, o]);
11561             Roo.callback(o.callback, o.scope, [o, true, r]);
11562
11563             setTimeout(function(){document.body.removeChild(frame);}, 100);
11564         }
11565
11566         Roo.EventManager.on(frame, 'load', cb, this);
11567         form.submit();
11568
11569         if(hiddens){ // remove dynamic params
11570             for(var i = 0, len = hiddens.length; i < len; i++){
11571                 form.removeChild(hiddens[i]);
11572             }
11573         }
11574     }
11575 });
11576 /*
11577  * Based on:
11578  * Ext JS Library 1.1.1
11579  * Copyright(c) 2006-2007, Ext JS, LLC.
11580  *
11581  * Originally Released Under LGPL - original licence link has changed is not relivant.
11582  *
11583  * Fork - LGPL
11584  * <script type="text/javascript">
11585  */
11586  
11587 /**
11588  * Global Ajax request class.
11589  * 
11590  * @class Roo.Ajax
11591  * @extends Roo.data.Connection
11592  * @static
11593  * 
11594  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11595  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11596  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11597  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11598  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11599  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11600  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11601  */
11602 Roo.Ajax = new Roo.data.Connection({
11603     // fix up the docs
11604     /**
11605      * @scope Roo.Ajax
11606      * @type {Boolear} 
11607      */
11608     autoAbort : false,
11609
11610     /**
11611      * Serialize the passed form into a url encoded string
11612      * @scope Roo.Ajax
11613      * @param {String/HTMLElement} form
11614      * @return {String}
11615      */
11616     serializeForm : function(form){
11617         return Roo.lib.Ajax.serializeForm(form);
11618     }
11619 });/*
11620  * Based on:
11621  * Ext JS Library 1.1.1
11622  * Copyright(c) 2006-2007, Ext JS, LLC.
11623  *
11624  * Originally Released Under LGPL - original licence link has changed is not relivant.
11625  *
11626  * Fork - LGPL
11627  * <script type="text/javascript">
11628  */
11629
11630  
11631 /**
11632  * @class Roo.UpdateManager
11633  * @extends Roo.util.Observable
11634  * Provides AJAX-style update for Element object.<br><br>
11635  * Usage:<br>
11636  * <pre><code>
11637  * // Get it from a Roo.Element object
11638  * var el = Roo.get("foo");
11639  * var mgr = el.getUpdateManager();
11640  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11641  * ...
11642  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11643  * <br>
11644  * // or directly (returns the same UpdateManager instance)
11645  * var mgr = new Roo.UpdateManager("myElementId");
11646  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11647  * mgr.on("update", myFcnNeedsToKnow);
11648  * <br>
11649    // short handed call directly from the element object
11650    Roo.get("foo").load({
11651         url: "bar.php",
11652         scripts:true,
11653         params: "for=bar",
11654         text: "Loading Foo..."
11655    });
11656  * </code></pre>
11657  * @constructor
11658  * Create new UpdateManager directly.
11659  * @param {String/HTMLElement/Roo.Element} el The element to update
11660  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11661  */
11662 Roo.UpdateManager = function(el, forceNew){
11663     el = Roo.get(el);
11664     if(!forceNew && el.updateManager){
11665         return el.updateManager;
11666     }
11667     /**
11668      * The Element object
11669      * @type Roo.Element
11670      */
11671     this.el = el;
11672     /**
11673      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11674      * @type String
11675      */
11676     this.defaultUrl = null;
11677
11678     this.addEvents({
11679         /**
11680          * @event beforeupdate
11681          * Fired before an update is made, return false from your handler and the update is cancelled.
11682          * @param {Roo.Element} el
11683          * @param {String/Object/Function} url
11684          * @param {String/Object} params
11685          */
11686         "beforeupdate": true,
11687         /**
11688          * @event update
11689          * Fired after successful update is made.
11690          * @param {Roo.Element} el
11691          * @param {Object} oResponseObject The response Object
11692          */
11693         "update": true,
11694         /**
11695          * @event failure
11696          * Fired on update failure.
11697          * @param {Roo.Element} el
11698          * @param {Object} oResponseObject The response Object
11699          */
11700         "failure": true
11701     });
11702     var d = Roo.UpdateManager.defaults;
11703     /**
11704      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11705      * @type String
11706      */
11707     this.sslBlankUrl = d.sslBlankUrl;
11708     /**
11709      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11710      * @type Boolean
11711      */
11712     this.disableCaching = d.disableCaching;
11713     /**
11714      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11715      * @type String
11716      */
11717     this.indicatorText = d.indicatorText;
11718     /**
11719      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11720      * @type String
11721      */
11722     this.showLoadIndicator = d.showLoadIndicator;
11723     /**
11724      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11725      * @type Number
11726      */
11727     this.timeout = d.timeout;
11728
11729     /**
11730      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11731      * @type Boolean
11732      */
11733     this.loadScripts = d.loadScripts;
11734
11735     /**
11736      * Transaction object of current executing transaction
11737      */
11738     this.transaction = null;
11739
11740     /**
11741      * @private
11742      */
11743     this.autoRefreshProcId = null;
11744     /**
11745      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.refreshDelegate = this.refresh.createDelegate(this);
11749     /**
11750      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11751      * @type Function
11752      */
11753     this.updateDelegate = this.update.createDelegate(this);
11754     /**
11755      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11756      * @type Function
11757      */
11758     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11759     /**
11760      * @private
11761      */
11762     this.successDelegate = this.processSuccess.createDelegate(this);
11763     /**
11764      * @private
11765      */
11766     this.failureDelegate = this.processFailure.createDelegate(this);
11767
11768     if(!this.renderer){
11769      /**
11770       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11771       */
11772     this.renderer = new Roo.UpdateManager.BasicRenderer();
11773     }
11774     
11775     Roo.UpdateManager.superclass.constructor.call(this);
11776 };
11777
11778 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11779     /**
11780      * Get the Element this UpdateManager is bound to
11781      * @return {Roo.Element} The element
11782      */
11783     getEl : function(){
11784         return this.el;
11785     },
11786     /**
11787      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11788      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11789 <pre><code>
11790 um.update({<br/>
11791     url: "your-url.php",<br/>
11792     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11793     callback: yourFunction,<br/>
11794     scope: yourObject, //(optional scope)  <br/>
11795     discardUrl: false, <br/>
11796     nocache: false,<br/>
11797     text: "Loading...",<br/>
11798     timeout: 30,<br/>
11799     scripts: false<br/>
11800 });
11801 </code></pre>
11802      * The only required property is url. The optional properties nocache, text and scripts
11803      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11804      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11805      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11806      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11807      */
11808     update : function(url, params, callback, discardUrl){
11809         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11810             var method = this.method,
11811                 cfg;
11812             if(typeof url == "object"){ // must be config object
11813                 cfg = url;
11814                 url = cfg.url;
11815                 params = params || cfg.params;
11816                 callback = callback || cfg.callback;
11817                 discardUrl = discardUrl || cfg.discardUrl;
11818                 if(callback && cfg.scope){
11819                     callback = callback.createDelegate(cfg.scope);
11820                 }
11821                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11822                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11823                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11824                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11825                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11826             }
11827             this.showLoading();
11828             if(!discardUrl){
11829                 this.defaultUrl = url;
11830             }
11831             if(typeof url == "function"){
11832                 url = url.call(this);
11833             }
11834
11835             method = method || (params ? "POST" : "GET");
11836             if(method == "GET"){
11837                 url = this.prepareUrl(url);
11838             }
11839
11840             var o = Roo.apply(cfg ||{}, {
11841                 url : url,
11842                 params: params,
11843                 success: this.successDelegate,
11844                 failure: this.failureDelegate,
11845                 callback: undefined,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11848             });
11849             Roo.log("updated manager called with timeout of " + o.timeout);
11850             this.transaction = Roo.Ajax.request(o);
11851         }
11852     },
11853
11854     /**
11855      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11856      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11857      * @param {String/HTMLElement} form The form Id or form element
11858      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11859      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11860      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11861      */
11862     formUpdate : function(form, url, reset, callback){
11863         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11864             if(typeof url == "function"){
11865                 url = url.call(this);
11866             }
11867             form = Roo.getDom(form);
11868             this.transaction = Roo.Ajax.request({
11869                 form: form,
11870                 url:url,
11871                 success: this.successDelegate,
11872                 failure: this.failureDelegate,
11873                 timeout: (this.timeout*1000),
11874                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11875             });
11876             this.showLoading.defer(1, this);
11877         }
11878     },
11879
11880     /**
11881      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11882      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11883      */
11884     refresh : function(callback){
11885         if(this.defaultUrl == null){
11886             return;
11887         }
11888         this.update(this.defaultUrl, null, callback, true);
11889     },
11890
11891     /**
11892      * Set this element to auto refresh.
11893      * @param {Number} interval How often to update (in seconds).
11894      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11895      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11896      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11897      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11898      */
11899     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11900         if(refreshNow){
11901             this.update(url || this.defaultUrl, params, callback, true);
11902         }
11903         if(this.autoRefreshProcId){
11904             clearInterval(this.autoRefreshProcId);
11905         }
11906         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11907     },
11908
11909     /**
11910      * Stop auto refresh on this element.
11911      */
11912      stopAutoRefresh : function(){
11913         if(this.autoRefreshProcId){
11914             clearInterval(this.autoRefreshProcId);
11915             delete this.autoRefreshProcId;
11916         }
11917     },
11918
11919     isAutoRefreshing : function(){
11920        return this.autoRefreshProcId ? true : false;
11921     },
11922     /**
11923      * Called to update the element to "Loading" state. Override to perform custom action.
11924      */
11925     showLoading : function(){
11926         if(this.showLoadIndicator){
11927             this.el.update(this.indicatorText);
11928         }
11929     },
11930
11931     /**
11932      * Adds unique parameter to query string if disableCaching = true
11933      * @private
11934      */
11935     prepareUrl : function(url){
11936         if(this.disableCaching){
11937             var append = "_dc=" + (new Date().getTime());
11938             if(url.indexOf("?") !== -1){
11939                 url += "&" + append;
11940             }else{
11941                 url += "?" + append;
11942             }
11943         }
11944         return url;
11945     },
11946
11947     /**
11948      * @private
11949      */
11950     processSuccess : function(response){
11951         this.transaction = null;
11952         if(response.argument.form && response.argument.reset){
11953             try{ // put in try/catch since some older FF releases had problems with this
11954                 response.argument.form.reset();
11955             }catch(e){}
11956         }
11957         if(this.loadScripts){
11958             this.renderer.render(this.el, response, this,
11959                 this.updateComplete.createDelegate(this, [response]));
11960         }else{
11961             this.renderer.render(this.el, response, this);
11962             this.updateComplete(response);
11963         }
11964     },
11965
11966     updateComplete : function(response){
11967         this.fireEvent("update", this.el, response);
11968         if(typeof response.argument.callback == "function"){
11969             response.argument.callback(this.el, true, response);
11970         }
11971     },
11972
11973     /**
11974      * @private
11975      */
11976     processFailure : function(response){
11977         this.transaction = null;
11978         this.fireEvent("failure", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, false, response);
11981         }
11982     },
11983
11984     /**
11985      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11986      * @param {Object} renderer The object implementing the render() method
11987      */
11988     setRenderer : function(renderer){
11989         this.renderer = renderer;
11990     },
11991
11992     getRenderer : function(){
11993        return this.renderer;
11994     },
11995
11996     /**
11997      * Set the defaultUrl used for updates
11998      * @param {String/Function} defaultUrl The url or a function to call to get the url
11999      */
12000     setDefaultUrl : function(defaultUrl){
12001         this.defaultUrl = defaultUrl;
12002     },
12003
12004     /**
12005      * Aborts the executing transaction
12006      */
12007     abort : function(){
12008         if(this.transaction){
12009             Roo.Ajax.abort(this.transaction);
12010         }
12011     },
12012
12013     /**
12014      * Returns true if an update is in progress
12015      * @return {Boolean}
12016      */
12017     isUpdating : function(){
12018         if(this.transaction){
12019             return Roo.Ajax.isLoading(this.transaction);
12020         }
12021         return false;
12022     }
12023 });
12024
12025 /**
12026  * @class Roo.UpdateManager.defaults
12027  * @static (not really - but it helps the doc tool)
12028  * The defaults collection enables customizing the default properties of UpdateManager
12029  */
12030    Roo.UpdateManager.defaults = {
12031        /**
12032          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12033          * @type Number
12034          */
12035          timeout : 30,
12036
12037          /**
12038          * True to process scripts by default (Defaults to false).
12039          * @type Boolean
12040          */
12041         loadScripts : false,
12042
12043         /**
12044         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12045         * @type String
12046         */
12047         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12048         /**
12049          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12050          * @type Boolean
12051          */
12052         disableCaching : false,
12053         /**
12054          * Whether to show indicatorText when loading (Defaults to true).
12055          * @type Boolean
12056          */
12057         showLoadIndicator : true,
12058         /**
12059          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12060          * @type String
12061          */
12062         indicatorText : '<div class="loading-indicator">Loading...</div>'
12063    };
12064
12065 /**
12066  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12067  *Usage:
12068  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12069  * @param {String/HTMLElement/Roo.Element} el The element to update
12070  * @param {String} url The url
12071  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12072  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12073  * @static
12074  * @deprecated
12075  * @member Roo.UpdateManager
12076  */
12077 Roo.UpdateManager.updateElement = function(el, url, params, options){
12078     var um = Roo.get(el, true).getUpdateManager();
12079     Roo.apply(um, options);
12080     um.update(url, params, options ? options.callback : null);
12081 };
12082 // alias for backwards compat
12083 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12084 /**
12085  * @class Roo.UpdateManager.BasicRenderer
12086  * Default Content renderer. Updates the elements innerHTML with the responseText.
12087  */
12088 Roo.UpdateManager.BasicRenderer = function(){};
12089
12090 Roo.UpdateManager.BasicRenderer.prototype = {
12091     /**
12092      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12093      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12094      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12095      * @param {Roo.Element} el The element being rendered
12096      * @param {Object} response The YUI Connect response object
12097      * @param {UpdateManager} updateManager The calling update manager
12098      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12099      */
12100      render : function(el, response, updateManager, callback){
12101         el.update(response.responseText, updateManager.loadScripts, callback);
12102     }
12103 };
12104 /*
12105  * Based on:
12106  * Roo JS
12107  * (c)) Alan Knowles
12108  * Licence : LGPL
12109  */
12110
12111
12112 /**
12113  * @class Roo.DomTemplate
12114  * @extends Roo.Template
12115  * An effort at a dom based template engine..
12116  *
12117  * Similar to XTemplate, except it uses dom parsing to create the template..
12118  *
12119  * Supported features:
12120  *
12121  *  Tags:
12122
12123 <pre><code>
12124       {a_variable} - output encoded.
12125       {a_variable.format:("Y-m-d")} - call a method on the variable
12126       {a_variable:raw} - unencoded output
12127       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12128       {a_variable:this.method_on_template(...)} - call a method on the template object.
12129  
12130 </code></pre>
12131  *  The tpl tag:
12132 <pre><code>
12133         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12134         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12135         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12136         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12137   
12138 </code></pre>
12139  *      
12140  */
12141 Roo.DomTemplate = function()
12142 {
12143      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12144      if (this.html) {
12145         this.compile();
12146      }
12147 };
12148
12149
12150 Roo.extend(Roo.DomTemplate, Roo.Template, {
12151     /**
12152      * id counter for sub templates.
12153      */
12154     id : 0,
12155     /**
12156      * flag to indicate if dom parser is inside a pre,
12157      * it will strip whitespace if not.
12158      */
12159     inPre : false,
12160     
12161     /**
12162      * The various sub templates
12163      */
12164     tpls : false,
12165     
12166     
12167     
12168     /**
12169      *
12170      * basic tag replacing syntax
12171      * WORD:WORD()
12172      *
12173      * // you can fake an object call by doing this
12174      *  x.t:(test,tesT) 
12175      * 
12176      */
12177     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12178     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12179     
12180     iterChild : function (node, method) {
12181         
12182         var oldPre = this.inPre;
12183         if (node.tagName == 'PRE') {
12184             this.inPre = true;
12185         }
12186         for( var i = 0; i < node.childNodes.length; i++) {
12187             method.call(this, node.childNodes[i]);
12188         }
12189         this.inPre = oldPre;
12190     },
12191     
12192     
12193     
12194     /**
12195      * compile the template
12196      *
12197      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12198      *
12199      */
12200     compile: function()
12201     {
12202         var s = this.html;
12203         
12204         // covert the html into DOM...
12205         var doc = false;
12206         var div =false;
12207         try {
12208             doc = document.implementation.createHTMLDocument("");
12209             doc.documentElement.innerHTML =   this.html  ;
12210             div = doc.documentElement;
12211         } catch (e) {
12212             // old IE... - nasty -- it causes all sorts of issues.. with
12213             // images getting pulled from server..
12214             div = document.createElement('div');
12215             div.innerHTML = this.html;
12216         }
12217         //doc.documentElement.innerHTML = htmlBody
12218          
12219         
12220         
12221         this.tpls = [];
12222         var _t = this;
12223         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12224         
12225         var tpls = this.tpls;
12226         
12227         // create a top level template from the snippet..
12228         
12229         //Roo.log(div.innerHTML);
12230         
12231         var tpl = {
12232             uid : 'master',
12233             id : this.id++,
12234             attr : false,
12235             value : false,
12236             body : div.innerHTML,
12237             
12238             forCall : false,
12239             execCall : false,
12240             dom : div,
12241             isTop : true
12242             
12243         };
12244         tpls.unshift(tpl);
12245         
12246         
12247         // compile them...
12248         this.tpls = [];
12249         Roo.each(tpls, function(tp){
12250             this.compileTpl(tp);
12251             this.tpls[tp.id] = tp;
12252         }, this);
12253         
12254         this.master = tpls[0];
12255         return this;
12256         
12257         
12258     },
12259     
12260     compileNode : function(node, istop) {
12261         // test for
12262         //Roo.log(node);
12263         
12264         
12265         // skip anything not a tag..
12266         if (node.nodeType != 1) {
12267             if (node.nodeType == 3 && !this.inPre) {
12268                 // reduce white space..
12269                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12270                 
12271             }
12272             return;
12273         }
12274         
12275         var tpl = {
12276             uid : false,
12277             id : false,
12278             attr : false,
12279             value : false,
12280             body : '',
12281             
12282             forCall : false,
12283             execCall : false,
12284             dom : false,
12285             isTop : istop
12286             
12287             
12288         };
12289         
12290         
12291         switch(true) {
12292             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12293             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12294             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12295             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12296             // no default..
12297         }
12298         
12299         
12300         if (!tpl.attr) {
12301             // just itterate children..
12302             this.iterChild(node,this.compileNode);
12303             return;
12304         }
12305         tpl.uid = this.id++;
12306         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12307         node.removeAttribute('roo-'+ tpl.attr);
12308         if (tpl.attr != 'name') {
12309             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12310             node.parentNode.replaceChild(placeholder,  node);
12311         } else {
12312             
12313             var placeholder =  document.createElement('span');
12314             placeholder.className = 'roo-tpl-' + tpl.value;
12315             node.parentNode.replaceChild(placeholder,  node);
12316         }
12317         
12318         // parent now sees '{domtplXXXX}
12319         this.iterChild(node,this.compileNode);
12320         
12321         // we should now have node body...
12322         var div = document.createElement('div');
12323         div.appendChild(node);
12324         tpl.dom = node;
12325         // this has the unfortunate side effect of converting tagged attributes
12326         // eg. href="{...}" into %7C...%7D
12327         // this has been fixed by searching for those combo's although it's a bit hacky..
12328         
12329         
12330         tpl.body = div.innerHTML;
12331         
12332         
12333          
12334         tpl.id = tpl.uid;
12335         switch(tpl.attr) {
12336             case 'for' :
12337                 switch (tpl.value) {
12338                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12339                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12340                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12341                 }
12342                 break;
12343             
12344             case 'exec':
12345                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12346                 break;
12347             
12348             case 'if':     
12349                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12350                 break;
12351             
12352             case 'name':
12353                 tpl.id  = tpl.value; // replace non characters???
12354                 break;
12355             
12356         }
12357         
12358         
12359         this.tpls.push(tpl);
12360         
12361         
12362         
12363     },
12364     
12365     
12366     
12367     
12368     /**
12369      * Compile a segment of the template into a 'sub-template'
12370      *
12371      * 
12372      * 
12373      *
12374      */
12375     compileTpl : function(tpl)
12376     {
12377         var fm = Roo.util.Format;
12378         var useF = this.disableFormats !== true;
12379         
12380         var sep = Roo.isGecko ? "+\n" : ",\n";
12381         
12382         var undef = function(str) {
12383             Roo.debug && Roo.log("Property not found :"  + str);
12384             return '';
12385         };
12386           
12387         //Roo.log(tpl.body);
12388         
12389         
12390         
12391         var fn = function(m, lbrace, name, format, args)
12392         {
12393             //Roo.log("ARGS");
12394             //Roo.log(arguments);
12395             args = args ? args.replace(/\\'/g,"'") : args;
12396             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12397             if (typeof(format) == 'undefined') {
12398                 format =  'htmlEncode'; 
12399             }
12400             if (format == 'raw' ) {
12401                 format = false;
12402             }
12403             
12404             if(name.substr(0, 6) == 'domtpl'){
12405                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12406             }
12407             
12408             // build an array of options to determine if value is undefined..
12409             
12410             // basically get 'xxxx.yyyy' then do
12411             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12412             //    (function () { Roo.log("Property not found"); return ''; })() :
12413             //    ......
12414             
12415             var udef_ar = [];
12416             var lookfor = '';
12417             Roo.each(name.split('.'), function(st) {
12418                 lookfor += (lookfor.length ? '.': '') + st;
12419                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12420             });
12421             
12422             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12423             
12424             
12425             if(format && useF){
12426                 
12427                 args = args ? ',' + args : "";
12428                  
12429                 if(format.substr(0, 5) != "this."){
12430                     format = "fm." + format + '(';
12431                 }else{
12432                     format = 'this.call("'+ format.substr(5) + '", ';
12433                     args = ", values";
12434                 }
12435                 
12436                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12437             }
12438              
12439             if (args && args.length) {
12440                 // called with xxyx.yuu:(test,test)
12441                 // change to ()
12442                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12443             }
12444             // raw.. - :raw modifier..
12445             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12446             
12447         };
12448         var body;
12449         // branched to use + in gecko and [].join() in others
12450         if(Roo.isGecko){
12451             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12452                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12453                     "';};};";
12454         }else{
12455             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12456             body.push(tpl.body.replace(/(\r\n|\n)/g,
12457                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12458             body.push("'].join('');};};");
12459             body = body.join('');
12460         }
12461         
12462         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12463        
12464         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12465         eval(body);
12466         
12467         return this;
12468     },
12469      
12470     /**
12471      * same as applyTemplate, except it's done to one of the subTemplates
12472      * when using named templates, you can do:
12473      *
12474      * var str = pl.applySubTemplate('your-name', values);
12475      *
12476      * 
12477      * @param {Number} id of the template
12478      * @param {Object} values to apply to template
12479      * @param {Object} parent (normaly the instance of this object)
12480      */
12481     applySubTemplate : function(id, values, parent)
12482     {
12483         
12484         
12485         var t = this.tpls[id];
12486         
12487         
12488         try { 
12489             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12490                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12491                 return '';
12492             }
12493         } catch(e) {
12494             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12495             Roo.log(values);
12496           
12497             return '';
12498         }
12499         try { 
12500             
12501             if(t.execCall && t.execCall.call(this, values, parent)){
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507             return '';
12508         }
12509         
12510         try {
12511             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12512             parent = t.target ? values : parent;
12513             if(t.forCall && vs instanceof Array){
12514                 var buf = [];
12515                 for(var i = 0, len = vs.length; i < len; i++){
12516                     try {
12517                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12518                     } catch (e) {
12519                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12520                         Roo.log(e.body);
12521                         //Roo.log(t.compiled);
12522                         Roo.log(vs[i]);
12523                     }   
12524                 }
12525                 return buf.join('');
12526             }
12527         } catch (e) {
12528             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12529             Roo.log(values);
12530             return '';
12531         }
12532         try {
12533             return t.compiled.call(this, vs, parent);
12534         } catch (e) {
12535             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12536             Roo.log(e.body);
12537             //Roo.log(t.compiled);
12538             Roo.log(values);
12539             return '';
12540         }
12541     },
12542
12543    
12544
12545     applyTemplate : function(values){
12546         return this.master.compiled.call(this, values, {});
12547         //var s = this.subs;
12548     },
12549
12550     apply : function(){
12551         return this.applyTemplate.apply(this, arguments);
12552     }
12553
12554  });
12555
12556 Roo.DomTemplate.from = function(el){
12557     el = Roo.getDom(el);
12558     return new Roo.Domtemplate(el.value || el.innerHTML);
12559 };/*
12560  * Based on:
12561  * Ext JS Library 1.1.1
12562  * Copyright(c) 2006-2007, Ext JS, LLC.
12563  *
12564  * Originally Released Under LGPL - original licence link has changed is not relivant.
12565  *
12566  * Fork - LGPL
12567  * <script type="text/javascript">
12568  */
12569
12570 /**
12571  * @class Roo.util.DelayedTask
12572  * Provides a convenient method of performing setTimeout where a new
12573  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12574  * You can use this class to buffer
12575  * the keypress events for a certain number of milliseconds, and perform only if they stop
12576  * for that amount of time.
12577  * @constructor The parameters to this constructor serve as defaults and are not required.
12578  * @param {Function} fn (optional) The default function to timeout
12579  * @param {Object} scope (optional) The default scope of that timeout
12580  * @param {Array} args (optional) The default Array of arguments
12581  */
12582 Roo.util.DelayedTask = function(fn, scope, args){
12583     var id = null, d, t;
12584
12585     var call = function(){
12586         var now = new Date().getTime();
12587         if(now - t >= d){
12588             clearInterval(id);
12589             id = null;
12590             fn.apply(scope, args || []);
12591         }
12592     };
12593     /**
12594      * Cancels any pending timeout and queues a new one
12595      * @param {Number} delay The milliseconds to delay
12596      * @param {Function} newFn (optional) Overrides function passed to constructor
12597      * @param {Object} newScope (optional) Overrides scope passed to constructor
12598      * @param {Array} newArgs (optional) Overrides args passed to constructor
12599      */
12600     this.delay = function(delay, newFn, newScope, newArgs){
12601         if(id && delay != d){
12602             this.cancel();
12603         }
12604         d = delay;
12605         t = new Date().getTime();
12606         fn = newFn || fn;
12607         scope = newScope || scope;
12608         args = newArgs || args;
12609         if(!id){
12610             id = setInterval(call, d);
12611         }
12612     };
12613
12614     /**
12615      * Cancel the last queued timeout
12616      */
12617     this.cancel = function(){
12618         if(id){
12619             clearInterval(id);
12620             id = null;
12621         }
12622     };
12623 };/*
12624  * Based on:
12625  * Ext JS Library 1.1.1
12626  * Copyright(c) 2006-2007, Ext JS, LLC.
12627  *
12628  * Originally Released Under LGPL - original licence link has changed is not relivant.
12629  *
12630  * Fork - LGPL
12631  * <script type="text/javascript">
12632  */
12633  
12634  
12635 Roo.util.TaskRunner = function(interval){
12636     interval = interval || 10;
12637     var tasks = [], removeQueue = [];
12638     var id = 0;
12639     var running = false;
12640
12641     var stopThread = function(){
12642         running = false;
12643         clearInterval(id);
12644         id = 0;
12645     };
12646
12647     var startThread = function(){
12648         if(!running){
12649             running = true;
12650             id = setInterval(runTasks, interval);
12651         }
12652     };
12653
12654     var removeTask = function(task){
12655         removeQueue.push(task);
12656         if(task.onStop){
12657             task.onStop();
12658         }
12659     };
12660
12661     var runTasks = function(){
12662         if(removeQueue.length > 0){
12663             for(var i = 0, len = removeQueue.length; i < len; i++){
12664                 tasks.remove(removeQueue[i]);
12665             }
12666             removeQueue = [];
12667             if(tasks.length < 1){
12668                 stopThread();
12669                 return;
12670             }
12671         }
12672         var now = new Date().getTime();
12673         for(var i = 0, len = tasks.length; i < len; ++i){
12674             var t = tasks[i];
12675             var itime = now - t.taskRunTime;
12676             if(t.interval <= itime){
12677                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12678                 t.taskRunTime = now;
12679                 if(rt === false || t.taskRunCount === t.repeat){
12680                     removeTask(t);
12681                     return;
12682                 }
12683             }
12684             if(t.duration && t.duration <= (now - t.taskStartTime)){
12685                 removeTask(t);
12686             }
12687         }
12688     };
12689
12690     /**
12691      * Queues a new task.
12692      * @param {Object} task
12693      */
12694     this.start = function(task){
12695         tasks.push(task);
12696         task.taskStartTime = new Date().getTime();
12697         task.taskRunTime = 0;
12698         task.taskRunCount = 0;
12699         startThread();
12700         return task;
12701     };
12702
12703     this.stop = function(task){
12704         removeTask(task);
12705         return task;
12706     };
12707
12708     this.stopAll = function(){
12709         stopThread();
12710         for(var i = 0, len = tasks.length; i < len; i++){
12711             if(tasks[i].onStop){
12712                 tasks[i].onStop();
12713             }
12714         }
12715         tasks = [];
12716         removeQueue = [];
12717     };
12718 };
12719
12720 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12721  * Based on:
12722  * Ext JS Library 1.1.1
12723  * Copyright(c) 2006-2007, Ext JS, LLC.
12724  *
12725  * Originally Released Under LGPL - original licence link has changed is not relivant.
12726  *
12727  * Fork - LGPL
12728  * <script type="text/javascript">
12729  */
12730
12731  
12732 /**
12733  * @class Roo.util.MixedCollection
12734  * @extends Roo.util.Observable
12735  * A Collection class that maintains both numeric indexes and keys and exposes events.
12736  * @constructor
12737  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12738  * collection (defaults to false)
12739  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12740  * and return the key value for that item.  This is used when available to look up the key on items that
12741  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12742  * equivalent to providing an implementation for the {@link #getKey} method.
12743  */
12744 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12745     this.items = [];
12746     this.map = {};
12747     this.keys = [];
12748     this.length = 0;
12749     this.addEvents({
12750         /**
12751          * @event clear
12752          * Fires when the collection is cleared.
12753          */
12754         "clear" : true,
12755         /**
12756          * @event add
12757          * Fires when an item is added to the collection.
12758          * @param {Number} index The index at which the item was added.
12759          * @param {Object} o The item added.
12760          * @param {String} key The key associated with the added item.
12761          */
12762         "add" : true,
12763         /**
12764          * @event replace
12765          * Fires when an item is replaced in the collection.
12766          * @param {String} key he key associated with the new added.
12767          * @param {Object} old The item being replaced.
12768          * @param {Object} new The new item.
12769          */
12770         "replace" : true,
12771         /**
12772          * @event remove
12773          * Fires when an item is removed from the collection.
12774          * @param {Object} o The item being removed.
12775          * @param {String} key (optional) The key associated with the removed item.
12776          */
12777         "remove" : true,
12778         "sort" : true
12779     });
12780     this.allowFunctions = allowFunctions === true;
12781     if(keyFn){
12782         this.getKey = keyFn;
12783     }
12784     Roo.util.MixedCollection.superclass.constructor.call(this);
12785 };
12786
12787 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12788     allowFunctions : false,
12789     
12790 /**
12791  * Adds an item to the collection.
12792  * @param {String} key The key to associate with the item
12793  * @param {Object} o The item to add.
12794  * @return {Object} The item added.
12795  */
12796     add : function(key, o){
12797         if(arguments.length == 1){
12798             o = arguments[0];
12799             key = this.getKey(o);
12800         }
12801         if(typeof key == "undefined" || key === null){
12802             this.length++;
12803             this.items.push(o);
12804             this.keys.push(null);
12805         }else{
12806             var old = this.map[key];
12807             if(old){
12808                 return this.replace(key, o);
12809             }
12810             this.length++;
12811             this.items.push(o);
12812             this.map[key] = o;
12813             this.keys.push(key);
12814         }
12815         this.fireEvent("add", this.length-1, o, key);
12816         return o;
12817     },
12818        
12819 /**
12820   * MixedCollection has a generic way to fetch keys if you implement getKey.
12821 <pre><code>
12822 // normal way
12823 var mc = new Roo.util.MixedCollection();
12824 mc.add(someEl.dom.id, someEl);
12825 mc.add(otherEl.dom.id, otherEl);
12826 //and so on
12827
12828 // using getKey
12829 var mc = new Roo.util.MixedCollection();
12830 mc.getKey = function(el){
12831    return el.dom.id;
12832 };
12833 mc.add(someEl);
12834 mc.add(otherEl);
12835
12836 // or via the constructor
12837 var mc = new Roo.util.MixedCollection(false, function(el){
12838    return el.dom.id;
12839 });
12840 mc.add(someEl);
12841 mc.add(otherEl);
12842 </code></pre>
12843  * @param o {Object} The item for which to find the key.
12844  * @return {Object} The key for the passed item.
12845  */
12846     getKey : function(o){
12847          return o.id; 
12848     },
12849    
12850 /**
12851  * Replaces an item in the collection.
12852  * @param {String} key The key associated with the item to replace, or the item to replace.
12853  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12854  * @return {Object}  The new item.
12855  */
12856     replace : function(key, o){
12857         if(arguments.length == 1){
12858             o = arguments[0];
12859             key = this.getKey(o);
12860         }
12861         var old = this.item(key);
12862         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12863              return this.add(key, o);
12864         }
12865         var index = this.indexOfKey(key);
12866         this.items[index] = o;
12867         this.map[key] = o;
12868         this.fireEvent("replace", key, old, o);
12869         return o;
12870     },
12871    
12872 /**
12873  * Adds all elements of an Array or an Object to the collection.
12874  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12875  * an Array of values, each of which are added to the collection.
12876  */
12877     addAll : function(objs){
12878         if(arguments.length > 1 || objs instanceof Array){
12879             var args = arguments.length > 1 ? arguments : objs;
12880             for(var i = 0, len = args.length; i < len; i++){
12881                 this.add(args[i]);
12882             }
12883         }else{
12884             for(var key in objs){
12885                 if(this.allowFunctions || typeof objs[key] != "function"){
12886                     this.add(key, objs[key]);
12887                 }
12888             }
12889         }
12890     },
12891    
12892 /**
12893  * Executes the specified function once for every item in the collection, passing each
12894  * item as the first and only parameter. returning false from the function will stop the iteration.
12895  * @param {Function} fn The function to execute for each item.
12896  * @param {Object} scope (optional) The scope in which to execute the function.
12897  */
12898     each : function(fn, scope){
12899         var items = [].concat(this.items); // each safe for removal
12900         for(var i = 0, len = items.length; i < len; i++){
12901             if(fn.call(scope || items[i], items[i], i, len) === false){
12902                 break;
12903             }
12904         }
12905     },
12906    
12907 /**
12908  * Executes the specified function once for every key in the collection, passing each
12909  * key, and its associated item as the first two parameters.
12910  * @param {Function} fn The function to execute for each item.
12911  * @param {Object} scope (optional) The scope in which to execute the function.
12912  */
12913     eachKey : function(fn, scope){
12914         for(var i = 0, len = this.keys.length; i < len; i++){
12915             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12916         }
12917     },
12918    
12919 /**
12920  * Returns the first item in the collection which elicits a true return value from the
12921  * passed selection function.
12922  * @param {Function} fn The selection function to execute for each item.
12923  * @param {Object} scope (optional) The scope in which to execute the function.
12924  * @return {Object} The first item in the collection which returned true from the selection function.
12925  */
12926     find : function(fn, scope){
12927         for(var i = 0, len = this.items.length; i < len; i++){
12928             if(fn.call(scope || window, this.items[i], this.keys[i])){
12929                 return this.items[i];
12930             }
12931         }
12932         return null;
12933     },
12934    
12935 /**
12936  * Inserts an item at the specified index in the collection.
12937  * @param {Number} index The index to insert the item at.
12938  * @param {String} key The key to associate with the new item, or the item itself.
12939  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12940  * @return {Object} The item inserted.
12941  */
12942     insert : function(index, key, o){
12943         if(arguments.length == 2){
12944             o = arguments[1];
12945             key = this.getKey(o);
12946         }
12947         if(index >= this.length){
12948             return this.add(key, o);
12949         }
12950         this.length++;
12951         this.items.splice(index, 0, o);
12952         if(typeof key != "undefined" && key != null){
12953             this.map[key] = o;
12954         }
12955         this.keys.splice(index, 0, key);
12956         this.fireEvent("add", index, o, key);
12957         return o;
12958     },
12959    
12960 /**
12961  * Removed an item from the collection.
12962  * @param {Object} o The item to remove.
12963  * @return {Object} The item removed.
12964  */
12965     remove : function(o){
12966         return this.removeAt(this.indexOf(o));
12967     },
12968    
12969 /**
12970  * Remove an item from a specified index in the collection.
12971  * @param {Number} index The index within the collection of the item to remove.
12972  */
12973     removeAt : function(index){
12974         if(index < this.length && index >= 0){
12975             this.length--;
12976             var o = this.items[index];
12977             this.items.splice(index, 1);
12978             var key = this.keys[index];
12979             if(typeof key != "undefined"){
12980                 delete this.map[key];
12981             }
12982             this.keys.splice(index, 1);
12983             this.fireEvent("remove", o, key);
12984         }
12985     },
12986    
12987 /**
12988  * Removed an item associated with the passed key fom the collection.
12989  * @param {String} key The key of the item to remove.
12990  */
12991     removeKey : function(key){
12992         return this.removeAt(this.indexOfKey(key));
12993     },
12994    
12995 /**
12996  * Returns the number of items in the collection.
12997  * @return {Number} the number of items in the collection.
12998  */
12999     getCount : function(){
13000         return this.length; 
13001     },
13002    
13003 /**
13004  * Returns index within the collection of the passed Object.
13005  * @param {Object} o The item to find the index of.
13006  * @return {Number} index of the item.
13007  */
13008     indexOf : function(o){
13009         if(!this.items.indexOf){
13010             for(var i = 0, len = this.items.length; i < len; i++){
13011                 if(this.items[i] == o) return i;
13012             }
13013             return -1;
13014         }else{
13015             return this.items.indexOf(o);
13016         }
13017     },
13018    
13019 /**
13020  * Returns index within the collection of the passed key.
13021  * @param {String} key The key to find the index of.
13022  * @return {Number} index of the key.
13023  */
13024     indexOfKey : function(key){
13025         if(!this.keys.indexOf){
13026             for(var i = 0, len = this.keys.length; i < len; i++){
13027                 if(this.keys[i] == key) return i;
13028             }
13029             return -1;
13030         }else{
13031             return this.keys.indexOf(key);
13032         }
13033     },
13034    
13035 /**
13036  * Returns the item associated with the passed key OR index. Key has priority over index.
13037  * @param {String/Number} key The key or index of the item.
13038  * @return {Object} The item associated with the passed key.
13039  */
13040     item : function(key){
13041         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13042         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13043     },
13044     
13045 /**
13046  * Returns the item at the specified index.
13047  * @param {Number} index The index of the item.
13048  * @return {Object}
13049  */
13050     itemAt : function(index){
13051         return this.items[index];
13052     },
13053     
13054 /**
13055  * Returns the item associated with the passed key.
13056  * @param {String/Number} key The key of the item.
13057  * @return {Object} The item associated with the passed key.
13058  */
13059     key : function(key){
13060         return this.map[key];
13061     },
13062    
13063 /**
13064  * Returns true if the collection contains the passed Object as an item.
13065  * @param {Object} o  The Object to look for in the collection.
13066  * @return {Boolean} True if the collection contains the Object as an item.
13067  */
13068     contains : function(o){
13069         return this.indexOf(o) != -1;
13070     },
13071    
13072 /**
13073  * Returns true if the collection contains the passed Object as a key.
13074  * @param {String} key The key to look for in the collection.
13075  * @return {Boolean} True if the collection contains the Object as a key.
13076  */
13077     containsKey : function(key){
13078         return typeof this.map[key] != "undefined";
13079     },
13080    
13081 /**
13082  * Removes all items from the collection.
13083  */
13084     clear : function(){
13085         this.length = 0;
13086         this.items = [];
13087         this.keys = [];
13088         this.map = {};
13089         this.fireEvent("clear");
13090     },
13091    
13092 /**
13093  * Returns the first item in the collection.
13094  * @return {Object} the first item in the collection..
13095  */
13096     first : function(){
13097         return this.items[0]; 
13098     },
13099    
13100 /**
13101  * Returns the last item in the collection.
13102  * @return {Object} the last item in the collection..
13103  */
13104     last : function(){
13105         return this.items[this.length-1];   
13106     },
13107     
13108     _sort : function(property, dir, fn){
13109         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13110         fn = fn || function(a, b){
13111             return a-b;
13112         };
13113         var c = [], k = this.keys, items = this.items;
13114         for(var i = 0, len = items.length; i < len; i++){
13115             c[c.length] = {key: k[i], value: items[i], index: i};
13116         }
13117         c.sort(function(a, b){
13118             var v = fn(a[property], b[property]) * dsc;
13119             if(v == 0){
13120                 v = (a.index < b.index ? -1 : 1);
13121             }
13122             return v;
13123         });
13124         for(var i = 0, len = c.length; i < len; i++){
13125             items[i] = c[i].value;
13126             k[i] = c[i].key;
13127         }
13128         this.fireEvent("sort", this);
13129     },
13130     
13131     /**
13132      * Sorts this collection with the passed comparison function
13133      * @param {String} direction (optional) "ASC" or "DESC"
13134      * @param {Function} fn (optional) comparison function
13135      */
13136     sort : function(dir, fn){
13137         this._sort("value", dir, fn);
13138     },
13139     
13140     /**
13141      * Sorts this collection by keys
13142      * @param {String} direction (optional) "ASC" or "DESC"
13143      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13144      */
13145     keySort : function(dir, fn){
13146         this._sort("key", dir, fn || function(a, b){
13147             return String(a).toUpperCase()-String(b).toUpperCase();
13148         });
13149     },
13150     
13151     /**
13152      * Returns a range of items in this collection
13153      * @param {Number} startIndex (optional) defaults to 0
13154      * @param {Number} endIndex (optional) default to the last item
13155      * @return {Array} An array of items
13156      */
13157     getRange : function(start, end){
13158         var items = this.items;
13159         if(items.length < 1){
13160             return [];
13161         }
13162         start = start || 0;
13163         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13164         var r = [];
13165         if(start <= end){
13166             for(var i = start; i <= end; i++) {
13167                     r[r.length] = items[i];
13168             }
13169         }else{
13170             for(var i = start; i >= end; i--) {
13171                     r[r.length] = items[i];
13172             }
13173         }
13174         return r;
13175     },
13176         
13177     /**
13178      * Filter the <i>objects</i> in this collection by a specific property. 
13179      * Returns a new collection that has been filtered.
13180      * @param {String} property A property on your objects
13181      * @param {String/RegExp} value Either string that the property values 
13182      * should start with or a RegExp to test against the property
13183      * @return {MixedCollection} The new filtered collection
13184      */
13185     filter : function(property, value){
13186         if(!value.exec){ // not a regex
13187             value = String(value);
13188             if(value.length == 0){
13189                 return this.clone();
13190             }
13191             value = new RegExp("^" + Roo.escapeRe(value), "i");
13192         }
13193         return this.filterBy(function(o){
13194             return o && value.test(o[property]);
13195         });
13196         },
13197     
13198     /**
13199      * Filter by a function. * Returns a new collection that has been filtered.
13200      * The passed function will be called with each 
13201      * object in the collection. If the function returns true, the value is included 
13202      * otherwise it is filtered.
13203      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13204      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13205      * @return {MixedCollection} The new filtered collection
13206      */
13207     filterBy : function(fn, scope){
13208         var r = new Roo.util.MixedCollection();
13209         r.getKey = this.getKey;
13210         var k = this.keys, it = this.items;
13211         for(var i = 0, len = it.length; i < len; i++){
13212             if(fn.call(scope||this, it[i], k[i])){
13213                                 r.add(k[i], it[i]);
13214                         }
13215         }
13216         return r;
13217     },
13218     
13219     /**
13220      * Creates a duplicate of this collection
13221      * @return {MixedCollection}
13222      */
13223     clone : function(){
13224         var r = new Roo.util.MixedCollection();
13225         var k = this.keys, it = this.items;
13226         for(var i = 0, len = it.length; i < len; i++){
13227             r.add(k[i], it[i]);
13228         }
13229         r.getKey = this.getKey;
13230         return r;
13231     }
13232 });
13233 /**
13234  * Returns the item associated with the passed key or index.
13235  * @method
13236  * @param {String/Number} key The key or index of the item.
13237  * @return {Object} The item associated with the passed key.
13238  */
13239 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13240  * Based on:
13241  * Ext JS Library 1.1.1
13242  * Copyright(c) 2006-2007, Ext JS, LLC.
13243  *
13244  * Originally Released Under LGPL - original licence link has changed is not relivant.
13245  *
13246  * Fork - LGPL
13247  * <script type="text/javascript">
13248  */
13249 /**
13250  * @class Roo.util.JSON
13251  * Modified version of Douglas Crockford"s json.js that doesn"t
13252  * mess with the Object prototype 
13253  * http://www.json.org/js.html
13254  * @singleton
13255  */
13256 Roo.util.JSON = new (function(){
13257     var useHasOwn = {}.hasOwnProperty ? true : false;
13258     
13259     // crashes Safari in some instances
13260     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13261     
13262     var pad = function(n) {
13263         return n < 10 ? "0" + n : n;
13264     };
13265     
13266     var m = {
13267         "\b": '\\b',
13268         "\t": '\\t',
13269         "\n": '\\n',
13270         "\f": '\\f',
13271         "\r": '\\r',
13272         '"' : '\\"',
13273         "\\": '\\\\'
13274     };
13275
13276     var encodeString = function(s){
13277         if (/["\\\x00-\x1f]/.test(s)) {
13278             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13279                 var c = m[b];
13280                 if(c){
13281                     return c;
13282                 }
13283                 c = b.charCodeAt();
13284                 return "\\u00" +
13285                     Math.floor(c / 16).toString(16) +
13286                     (c % 16).toString(16);
13287             }) + '"';
13288         }
13289         return '"' + s + '"';
13290     };
13291     
13292     var encodeArray = function(o){
13293         var a = ["["], b, i, l = o.length, v;
13294             for (i = 0; i < l; i += 1) {
13295                 v = o[i];
13296                 switch (typeof v) {
13297                     case "undefined":
13298                     case "function":
13299                     case "unknown":
13300                         break;
13301                     default:
13302                         if (b) {
13303                             a.push(',');
13304                         }
13305                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13306                         b = true;
13307                 }
13308             }
13309             a.push("]");
13310             return a.join("");
13311     };
13312     
13313     var encodeDate = function(o){
13314         return '"' + o.getFullYear() + "-" +
13315                 pad(o.getMonth() + 1) + "-" +
13316                 pad(o.getDate()) + "T" +
13317                 pad(o.getHours()) + ":" +
13318                 pad(o.getMinutes()) + ":" +
13319                 pad(o.getSeconds()) + '"';
13320     };
13321     
13322     /**
13323      * Encodes an Object, Array or other value
13324      * @param {Mixed} o The variable to encode
13325      * @return {String} The JSON string
13326      */
13327     this.encode = function(o)
13328     {
13329         // should this be extended to fully wrap stringify..
13330         
13331         if(typeof o == "undefined" || o === null){
13332             return "null";
13333         }else if(o instanceof Array){
13334             return encodeArray(o);
13335         }else if(o instanceof Date){
13336             return encodeDate(o);
13337         }else if(typeof o == "string"){
13338             return encodeString(o);
13339         }else if(typeof o == "number"){
13340             return isFinite(o) ? String(o) : "null";
13341         }else if(typeof o == "boolean"){
13342             return String(o);
13343         }else {
13344             var a = ["{"], b, i, v;
13345             for (i in o) {
13346                 if(!useHasOwn || o.hasOwnProperty(i)) {
13347                     v = o[i];
13348                     switch (typeof v) {
13349                     case "undefined":
13350                     case "function":
13351                     case "unknown":
13352                         break;
13353                     default:
13354                         if(b){
13355                             a.push(',');
13356                         }
13357                         a.push(this.encode(i), ":",
13358                                 v === null ? "null" : this.encode(v));
13359                         b = true;
13360                     }
13361                 }
13362             }
13363             a.push("}");
13364             return a.join("");
13365         }
13366     };
13367     
13368     /**
13369      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13370      * @param {String} json The JSON string
13371      * @return {Object} The resulting object
13372      */
13373     this.decode = function(json){
13374         
13375         return  /** eval:var:json */ eval("(" + json + ')');
13376     };
13377 })();
13378 /** 
13379  * Shorthand for {@link Roo.util.JSON#encode}
13380  * @member Roo encode 
13381  * @method */
13382 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13383 /** 
13384  * Shorthand for {@link Roo.util.JSON#decode}
13385  * @member Roo decode 
13386  * @method */
13387 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13388 /*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398  
13399 /**
13400  * @class Roo.util.Format
13401  * Reusable data formatting functions
13402  * @singleton
13403  */
13404 Roo.util.Format = function(){
13405     var trimRe = /^\s+|\s+$/g;
13406     return {
13407         /**
13408          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13409          * @param {String} value The string to truncate
13410          * @param {Number} length The maximum length to allow before truncating
13411          * @return {String} The converted text
13412          */
13413         ellipsis : function(value, len){
13414             if(value && value.length > len){
13415                 return value.substr(0, len-3)+"...";
13416             }
13417             return value;
13418         },
13419
13420         /**
13421          * Checks a reference and converts it to empty string if it is undefined
13422          * @param {Mixed} value Reference to check
13423          * @return {Mixed} Empty string if converted, otherwise the original value
13424          */
13425         undef : function(value){
13426             return typeof value != "undefined" ? value : "";
13427         },
13428
13429         /**
13430          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13431          * @param {String} value The string to encode
13432          * @return {String} The encoded text
13433          */
13434         htmlEncode : function(value){
13435             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13436         },
13437
13438         /**
13439          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13440          * @param {String} value The string to decode
13441          * @return {String} The decoded text
13442          */
13443         htmlDecode : function(value){
13444             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13445         },
13446
13447         /**
13448          * Trims any whitespace from either side of a string
13449          * @param {String} value The text to trim
13450          * @return {String} The trimmed text
13451          */
13452         trim : function(value){
13453             return String(value).replace(trimRe, "");
13454         },
13455
13456         /**
13457          * Returns a substring from within an original string
13458          * @param {String} value The original text
13459          * @param {Number} start The start index of the substring
13460          * @param {Number} length The length of the substring
13461          * @return {String} The substring
13462          */
13463         substr : function(value, start, length){
13464             return String(value).substr(start, length);
13465         },
13466
13467         /**
13468          * Converts a string to all lower case letters
13469          * @param {String} value The text to convert
13470          * @return {String} The converted text
13471          */
13472         lowercase : function(value){
13473             return String(value).toLowerCase();
13474         },
13475
13476         /**
13477          * Converts a string to all upper case letters
13478          * @param {String} value The text to convert
13479          * @return {String} The converted text
13480          */
13481         uppercase : function(value){
13482             return String(value).toUpperCase();
13483         },
13484
13485         /**
13486          * Converts the first character only of a string to upper case
13487          * @param {String} value The text to convert
13488          * @return {String} The converted text
13489          */
13490         capitalize : function(value){
13491             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13492         },
13493
13494         // private
13495         call : function(value, fn){
13496             if(arguments.length > 2){
13497                 var args = Array.prototype.slice.call(arguments, 2);
13498                 args.unshift(value);
13499                  
13500                 return /** eval:var:value */  eval(fn).apply(window, args);
13501             }else{
13502                 /** eval:var:value */
13503                 return /** eval:var:value */ eval(fn).call(window, value);
13504             }
13505         },
13506
13507        
13508         /**
13509          * safer version of Math.toFixed..??/
13510          * @param {Number/String} value The numeric value to format
13511          * @param {Number/String} value Decimal places 
13512          * @return {String} The formatted currency string
13513          */
13514         toFixed : function(v, n)
13515         {
13516             // why not use to fixed - precision is buggered???
13517             if (!n) {
13518                 return Math.round(v-0);
13519             }
13520             var fact = Math.pow(10,n+1);
13521             v = (Math.round((v-0)*fact))/fact;
13522             var z = (''+fact).substring(2);
13523             if (v == Math.floor(v)) {
13524                 return Math.floor(v) + '.' + z;
13525             }
13526             
13527             // now just padd decimals..
13528             var ps = String(v).split('.');
13529             var fd = (ps[1] + z);
13530             var r = fd.substring(0,n); 
13531             var rm = fd.substring(n); 
13532             if (rm < 5) {
13533                 return ps[0] + '.' + r;
13534             }
13535             r*=1; // turn it into a number;
13536             r++;
13537             if (String(r).length != n) {
13538                 ps[0]*=1;
13539                 ps[0]++;
13540                 r = String(r).substring(1); // chop the end off.
13541             }
13542             
13543             return ps[0] + '.' + r;
13544              
13545         },
13546         
13547         /**
13548          * Format a number as US currency
13549          * @param {Number/String} value The numeric value to format
13550          * @return {String} The formatted currency string
13551          */
13552         usMoney : function(v){
13553             return '$' + Roo.util.Format.number(v);
13554         },
13555         
13556         /**
13557          * Format a number
13558          * eventually this should probably emulate php's number_format
13559          * @param {Number/String} value The numeric value to format
13560          * @param {Number} decimals number of decimal places
13561          * @return {String} The formatted currency string
13562          */
13563         number : function(v,decimals)
13564         {
13565             // multiply and round.
13566             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13567             var mul = Math.pow(10, decimals);
13568             var zero = String(mul).substring(1);
13569             v = (Math.round((v-0)*mul))/mul;
13570             
13571             // if it's '0' number.. then
13572             
13573             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13574             v = String(v);
13575             var ps = v.split('.');
13576             var whole = ps[0];
13577             
13578             
13579             var r = /(\d+)(\d{3})/;
13580             // add comma's
13581             while (r.test(whole)) {
13582                 whole = whole.replace(r, '$1' + ',' + '$2');
13583             }
13584             
13585             
13586             var sub = ps[1] ?
13587                     // has decimals..
13588                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13589                     // does not have decimals
13590                     (decimals ? ('.' + zero) : '');
13591             
13592             
13593             return whole + sub ;
13594         },
13595         
13596         /**
13597          * Parse a value into a formatted date using the specified format pattern.
13598          * @param {Mixed} value The value to format
13599          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13600          * @return {String} The formatted date string
13601          */
13602         date : function(v, format){
13603             if(!v){
13604                 return "";
13605             }
13606             if(!(v instanceof Date)){
13607                 v = new Date(Date.parse(v));
13608             }
13609             return v.dateFormat(format || Roo.util.Format.defaults.date);
13610         },
13611
13612         /**
13613          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13614          * @param {String} format Any valid date format string
13615          * @return {Function} The date formatting function
13616          */
13617         dateRenderer : function(format){
13618             return function(v){
13619                 return Roo.util.Format.date(v, format);  
13620             };
13621         },
13622
13623         // private
13624         stripTagsRE : /<\/?[^>]+>/gi,
13625         
13626         /**
13627          * Strips all HTML tags
13628          * @param {Mixed} value The text from which to strip tags
13629          * @return {String} The stripped text
13630          */
13631         stripTags : function(v){
13632             return !v ? v : String(v).replace(this.stripTagsRE, "");
13633         }
13634     };
13635 }();
13636 Roo.util.Format.defaults = {
13637     date : 'd/M/Y'
13638 };/*
13639  * Based on:
13640  * Ext JS Library 1.1.1
13641  * Copyright(c) 2006-2007, Ext JS, LLC.
13642  *
13643  * Originally Released Under LGPL - original licence link has changed is not relivant.
13644  *
13645  * Fork - LGPL
13646  * <script type="text/javascript">
13647  */
13648
13649
13650  
13651
13652 /**
13653  * @class Roo.MasterTemplate
13654  * @extends Roo.Template
13655  * Provides a template that can have child templates. The syntax is:
13656 <pre><code>
13657 var t = new Roo.MasterTemplate(
13658         '&lt;select name="{name}"&gt;',
13659                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13660         '&lt;/select&gt;'
13661 );
13662 t.add('options', {value: 'foo', text: 'bar'});
13663 // or you can add multiple child elements in one shot
13664 t.addAll('options', [
13665     {value: 'foo', text: 'bar'},
13666     {value: 'foo2', text: 'bar2'},
13667     {value: 'foo3', text: 'bar3'}
13668 ]);
13669 // then append, applying the master template values
13670 t.append('my-form', {name: 'my-select'});
13671 </code></pre>
13672 * A name attribute for the child template is not required if you have only one child
13673 * template or you want to refer to them by index.
13674  */
13675 Roo.MasterTemplate = function(){
13676     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13677     this.originalHtml = this.html;
13678     var st = {};
13679     var m, re = this.subTemplateRe;
13680     re.lastIndex = 0;
13681     var subIndex = 0;
13682     while(m = re.exec(this.html)){
13683         var name = m[1], content = m[2];
13684         st[subIndex] = {
13685             name: name,
13686             index: subIndex,
13687             buffer: [],
13688             tpl : new Roo.Template(content)
13689         };
13690         if(name){
13691             st[name] = st[subIndex];
13692         }
13693         st[subIndex].tpl.compile();
13694         st[subIndex].tpl.call = this.call.createDelegate(this);
13695         subIndex++;
13696     }
13697     this.subCount = subIndex;
13698     this.subs = st;
13699 };
13700 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13701     /**
13702     * The regular expression used to match sub templates
13703     * @type RegExp
13704     * @property
13705     */
13706     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13707
13708     /**
13709      * Applies the passed values to a child template.
13710      * @param {String/Number} name (optional) The name or index of the child template
13711      * @param {Array/Object} values The values to be applied to the template
13712      * @return {MasterTemplate} this
13713      */
13714      add : function(name, values){
13715         if(arguments.length == 1){
13716             values = arguments[0];
13717             name = 0;
13718         }
13719         var s = this.subs[name];
13720         s.buffer[s.buffer.length] = s.tpl.apply(values);
13721         return this;
13722     },
13723
13724     /**
13725      * Applies all the passed values to a child template.
13726      * @param {String/Number} name (optional) The name or index of the child template
13727      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13728      * @param {Boolean} reset (optional) True to reset the template first
13729      * @return {MasterTemplate} this
13730      */
13731     fill : function(name, values, reset){
13732         var a = arguments;
13733         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13734             values = a[0];
13735             name = 0;
13736             reset = a[1];
13737         }
13738         if(reset){
13739             this.reset();
13740         }
13741         for(var i = 0, len = values.length; i < len; i++){
13742             this.add(name, values[i]);
13743         }
13744         return this;
13745     },
13746
13747     /**
13748      * Resets the template for reuse
13749      * @return {MasterTemplate} this
13750      */
13751      reset : function(){
13752         var s = this.subs;
13753         for(var i = 0; i < this.subCount; i++){
13754             s[i].buffer = [];
13755         }
13756         return this;
13757     },
13758
13759     applyTemplate : function(values){
13760         var s = this.subs;
13761         var replaceIndex = -1;
13762         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13763             return s[++replaceIndex].buffer.join("");
13764         });
13765         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13766     },
13767
13768     apply : function(){
13769         return this.applyTemplate.apply(this, arguments);
13770     },
13771
13772     compile : function(){return this;}
13773 });
13774
13775 /**
13776  * Alias for fill().
13777  * @method
13778  */
13779 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13780  /**
13781  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13782  * var tpl = Roo.MasterTemplate.from('element-id');
13783  * @param {String/HTMLElement} el
13784  * @param {Object} config
13785  * @static
13786  */
13787 Roo.MasterTemplate.from = function(el, config){
13788     el = Roo.getDom(el);
13789     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13790 };/*
13791  * Based on:
13792  * Ext JS Library 1.1.1
13793  * Copyright(c) 2006-2007, Ext JS, LLC.
13794  *
13795  * Originally Released Under LGPL - original licence link has changed is not relivant.
13796  *
13797  * Fork - LGPL
13798  * <script type="text/javascript">
13799  */
13800
13801  
13802 /**
13803  * @class Roo.util.CSS
13804  * Utility class for manipulating CSS rules
13805  * @singleton
13806  */
13807 Roo.util.CSS = function(){
13808         var rules = null;
13809         var doc = document;
13810
13811     var camelRe = /(-[a-z])/gi;
13812     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13813
13814    return {
13815    /**
13816     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13817     * tag and appended to the HEAD of the document.
13818     * @param {String|Object} cssText The text containing the css rules
13819     * @param {String} id An id to add to the stylesheet for later removal
13820     * @return {StyleSheet}
13821     */
13822     createStyleSheet : function(cssText, id){
13823         var ss;
13824         var head = doc.getElementsByTagName("head")[0];
13825         var nrules = doc.createElement("style");
13826         nrules.setAttribute("type", "text/css");
13827         if(id){
13828             nrules.setAttribute("id", id);
13829         }
13830         if (typeof(cssText) != 'string') {
13831             // support object maps..
13832             // not sure if this a good idea.. 
13833             // perhaps it should be merged with the general css handling
13834             // and handle js style props.
13835             var cssTextNew = [];
13836             for(var n in cssText) {
13837                 var citems = [];
13838                 for(var k in cssText[n]) {
13839                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13840                 }
13841                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13842                 
13843             }
13844             cssText = cssTextNew.join("\n");
13845             
13846         }
13847        
13848        
13849        if(Roo.isIE){
13850            head.appendChild(nrules);
13851            ss = nrules.styleSheet;
13852            ss.cssText = cssText;
13853        }else{
13854            try{
13855                 nrules.appendChild(doc.createTextNode(cssText));
13856            }catch(e){
13857                nrules.cssText = cssText; 
13858            }
13859            head.appendChild(nrules);
13860            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13861        }
13862        this.cacheStyleSheet(ss);
13863        return ss;
13864    },
13865
13866    /**
13867     * Removes a style or link tag by id
13868     * @param {String} id The id of the tag
13869     */
13870    removeStyleSheet : function(id){
13871        var existing = doc.getElementById(id);
13872        if(existing){
13873            existing.parentNode.removeChild(existing);
13874        }
13875    },
13876
13877    /**
13878     * Dynamically swaps an existing stylesheet reference for a new one
13879     * @param {String} id The id of an existing link tag to remove
13880     * @param {String} url The href of the new stylesheet to include
13881     */
13882    swapStyleSheet : function(id, url){
13883        this.removeStyleSheet(id);
13884        var ss = doc.createElement("link");
13885        ss.setAttribute("rel", "stylesheet");
13886        ss.setAttribute("type", "text/css");
13887        ss.setAttribute("id", id);
13888        ss.setAttribute("href", url);
13889        doc.getElementsByTagName("head")[0].appendChild(ss);
13890    },
13891    
13892    /**
13893     * Refresh the rule cache if you have dynamically added stylesheets
13894     * @return {Object} An object (hash) of rules indexed by selector
13895     */
13896    refreshCache : function(){
13897        return this.getRules(true);
13898    },
13899
13900    // private
13901    cacheStyleSheet : function(stylesheet){
13902        if(!rules){
13903            rules = {};
13904        }
13905        try{// try catch for cross domain access issue
13906            var ssRules = stylesheet.cssRules || stylesheet.rules;
13907            for(var j = ssRules.length-1; j >= 0; --j){
13908                rules[ssRules[j].selectorText] = ssRules[j];
13909            }
13910        }catch(e){}
13911    },
13912    
13913    /**
13914     * Gets all css rules for the document
13915     * @param {Boolean} refreshCache true to refresh the internal cache
13916     * @return {Object} An object (hash) of rules indexed by selector
13917     */
13918    getRules : function(refreshCache){
13919                 if(rules == null || refreshCache){
13920                         rules = {};
13921                         var ds = doc.styleSheets;
13922                         for(var i =0, len = ds.length; i < len; i++){
13923                             try{
13924                         this.cacheStyleSheet(ds[i]);
13925                     }catch(e){} 
13926                 }
13927                 }
13928                 return rules;
13929         },
13930         
13931         /**
13932     * Gets an an individual CSS rule by selector(s)
13933     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13934     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13935     * @return {CSSRule} The CSS rule or null if one is not found
13936     */
13937    getRule : function(selector, refreshCache){
13938                 var rs = this.getRules(refreshCache);
13939                 if(!(selector instanceof Array)){
13940                     return rs[selector];
13941                 }
13942                 for(var i = 0; i < selector.length; i++){
13943                         if(rs[selector[i]]){
13944                                 return rs[selector[i]];
13945                         }
13946                 }
13947                 return null;
13948         },
13949         
13950         
13951         /**
13952     * Updates a rule property
13953     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13954     * @param {String} property The css property
13955     * @param {String} value The new value for the property
13956     * @return {Boolean} true If a rule was found and updated
13957     */
13958    updateRule : function(selector, property, value){
13959                 if(!(selector instanceof Array)){
13960                         var rule = this.getRule(selector);
13961                         if(rule){
13962                                 rule.style[property.replace(camelRe, camelFn)] = value;
13963                                 return true;
13964                         }
13965                 }else{
13966                         for(var i = 0; i < selector.length; i++){
13967                                 if(this.updateRule(selector[i], property, value)){
13968                                         return true;
13969                                 }
13970                         }
13971                 }
13972                 return false;
13973         }
13974    };   
13975 }();/*
13976  * Based on:
13977  * Ext JS Library 1.1.1
13978  * Copyright(c) 2006-2007, Ext JS, LLC.
13979  *
13980  * Originally Released Under LGPL - original licence link has changed is not relivant.
13981  *
13982  * Fork - LGPL
13983  * <script type="text/javascript">
13984  */
13985
13986  
13987
13988 /**
13989  * @class Roo.util.ClickRepeater
13990  * @extends Roo.util.Observable
13991  * 
13992  * A wrapper class which can be applied to any element. Fires a "click" event while the
13993  * mouse is pressed. The interval between firings may be specified in the config but
13994  * defaults to 10 milliseconds.
13995  * 
13996  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13997  * 
13998  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13999  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14000  * Similar to an autorepeat key delay.
14001  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14002  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14003  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14004  *           "interval" and "delay" are ignored. "immediate" is honored.
14005  * @cfg {Boolean} preventDefault True to prevent the default click event
14006  * @cfg {Boolean} stopDefault True to stop the default click event
14007  * 
14008  * @history
14009  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14010  *     2007-02-02 jvs Renamed to ClickRepeater
14011  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14012  *
14013  *  @constructor
14014  * @param {String/HTMLElement/Element} el The element to listen on
14015  * @param {Object} config
14016  **/
14017 Roo.util.ClickRepeater = function(el, config)
14018 {
14019     this.el = Roo.get(el);
14020     this.el.unselectable();
14021
14022     Roo.apply(this, config);
14023
14024     this.addEvents({
14025     /**
14026      * @event mousedown
14027      * Fires when the mouse button is depressed.
14028      * @param {Roo.util.ClickRepeater} this
14029      */
14030         "mousedown" : true,
14031     /**
14032      * @event click
14033      * Fires on a specified interval during the time the element is pressed.
14034      * @param {Roo.util.ClickRepeater} this
14035      */
14036         "click" : true,
14037     /**
14038      * @event mouseup
14039      * Fires when the mouse key is released.
14040      * @param {Roo.util.ClickRepeater} this
14041      */
14042         "mouseup" : true
14043     });
14044
14045     this.el.on("mousedown", this.handleMouseDown, this);
14046     if(this.preventDefault || this.stopDefault){
14047         this.el.on("click", function(e){
14048             if(this.preventDefault){
14049                 e.preventDefault();
14050             }
14051             if(this.stopDefault){
14052                 e.stopEvent();
14053             }
14054         }, this);
14055     }
14056
14057     // allow inline handler
14058     if(this.handler){
14059         this.on("click", this.handler,  this.scope || this);
14060     }
14061
14062     Roo.util.ClickRepeater.superclass.constructor.call(this);
14063 };
14064
14065 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14066     interval : 20,
14067     delay: 250,
14068     preventDefault : true,
14069     stopDefault : false,
14070     timer : 0,
14071
14072     // private
14073     handleMouseDown : function(){
14074         clearTimeout(this.timer);
14075         this.el.blur();
14076         if(this.pressClass){
14077             this.el.addClass(this.pressClass);
14078         }
14079         this.mousedownTime = new Date();
14080
14081         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14082         this.el.on("mouseout", this.handleMouseOut, this);
14083
14084         this.fireEvent("mousedown", this);
14085         this.fireEvent("click", this);
14086         
14087         this.timer = this.click.defer(this.delay || this.interval, this);
14088     },
14089
14090     // private
14091     click : function(){
14092         this.fireEvent("click", this);
14093         this.timer = this.click.defer(this.getInterval(), this);
14094     },
14095
14096     // private
14097     getInterval: function(){
14098         if(!this.accelerate){
14099             return this.interval;
14100         }
14101         var pressTime = this.mousedownTime.getElapsed();
14102         if(pressTime < 500){
14103             return 400;
14104         }else if(pressTime < 1700){
14105             return 320;
14106         }else if(pressTime < 2600){
14107             return 250;
14108         }else if(pressTime < 3500){
14109             return 180;
14110         }else if(pressTime < 4400){
14111             return 140;
14112         }else if(pressTime < 5300){
14113             return 80;
14114         }else if(pressTime < 6200){
14115             return 50;
14116         }else{
14117             return 10;
14118         }
14119     },
14120
14121     // private
14122     handleMouseOut : function(){
14123         clearTimeout(this.timer);
14124         if(this.pressClass){
14125             this.el.removeClass(this.pressClass);
14126         }
14127         this.el.on("mouseover", this.handleMouseReturn, this);
14128     },
14129
14130     // private
14131     handleMouseReturn : function(){
14132         this.el.un("mouseover", this.handleMouseReturn);
14133         if(this.pressClass){
14134             this.el.addClass(this.pressClass);
14135         }
14136         this.click();
14137     },
14138
14139     // private
14140     handleMouseUp : function(){
14141         clearTimeout(this.timer);
14142         this.el.un("mouseover", this.handleMouseReturn);
14143         this.el.un("mouseout", this.handleMouseOut);
14144         Roo.get(document).un("mouseup", this.handleMouseUp);
14145         this.el.removeClass(this.pressClass);
14146         this.fireEvent("mouseup", this);
14147     }
14148 });/*
14149  * Based on:
14150  * Ext JS Library 1.1.1
14151  * Copyright(c) 2006-2007, Ext JS, LLC.
14152  *
14153  * Originally Released Under LGPL - original licence link has changed is not relivant.
14154  *
14155  * Fork - LGPL
14156  * <script type="text/javascript">
14157  */
14158
14159  
14160 /**
14161  * @class Roo.KeyNav
14162  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14163  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14164  * way to implement custom navigation schemes for any UI component.</p>
14165  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14166  * pageUp, pageDown, del, home, end.  Usage:</p>
14167  <pre><code>
14168 var nav = new Roo.KeyNav("my-element", {
14169     "left" : function(e){
14170         this.moveLeft(e.ctrlKey);
14171     },
14172     "right" : function(e){
14173         this.moveRight(e.ctrlKey);
14174     },
14175     "enter" : function(e){
14176         this.save();
14177     },
14178     scope : this
14179 });
14180 </code></pre>
14181  * @constructor
14182  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14183  * @param {Object} config The config
14184  */
14185 Roo.KeyNav = function(el, config){
14186     this.el = Roo.get(el);
14187     Roo.apply(this, config);
14188     if(!this.disabled){
14189         this.disabled = true;
14190         this.enable();
14191     }
14192 };
14193
14194 Roo.KeyNav.prototype = {
14195     /**
14196      * @cfg {Boolean} disabled
14197      * True to disable this KeyNav instance (defaults to false)
14198      */
14199     disabled : false,
14200     /**
14201      * @cfg {String} defaultEventAction
14202      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14203      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14204      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14205      */
14206     defaultEventAction: "stopEvent",
14207     /**
14208      * @cfg {Boolean} forceKeyDown
14209      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14210      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14211      * handle keydown instead of keypress.
14212      */
14213     forceKeyDown : false,
14214
14215     // private
14216     prepareEvent : function(e){
14217         var k = e.getKey();
14218         var h = this.keyToHandler[k];
14219         //if(h && this[h]){
14220         //    e.stopPropagation();
14221         //}
14222         if(Roo.isSafari && h && k >= 37 && k <= 40){
14223             e.stopEvent();
14224         }
14225     },
14226
14227     // private
14228     relay : function(e){
14229         var k = e.getKey();
14230         var h = this.keyToHandler[k];
14231         if(h && this[h]){
14232             if(this.doRelay(e, this[h], h) !== true){
14233                 e[this.defaultEventAction]();
14234             }
14235         }
14236     },
14237
14238     // private
14239     doRelay : function(e, h, hname){
14240         return h.call(this.scope || this, e);
14241     },
14242
14243     // possible handlers
14244     enter : false,
14245     left : false,
14246     right : false,
14247     up : false,
14248     down : false,
14249     tab : false,
14250     esc : false,
14251     pageUp : false,
14252     pageDown : false,
14253     del : false,
14254     home : false,
14255     end : false,
14256
14257     // quick lookup hash
14258     keyToHandler : {
14259         37 : "left",
14260         39 : "right",
14261         38 : "up",
14262         40 : "down",
14263         33 : "pageUp",
14264         34 : "pageDown",
14265         46 : "del",
14266         36 : "home",
14267         35 : "end",
14268         13 : "enter",
14269         27 : "esc",
14270         9  : "tab"
14271     },
14272
14273         /**
14274          * Enable this KeyNav
14275          */
14276         enable: function(){
14277                 if(this.disabled){
14278             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14279             // the EventObject will normalize Safari automatically
14280             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14281                 this.el.on("keydown", this.relay,  this);
14282             }else{
14283                 this.el.on("keydown", this.prepareEvent,  this);
14284                 this.el.on("keypress", this.relay,  this);
14285             }
14286                     this.disabled = false;
14287                 }
14288         },
14289
14290         /**
14291          * Disable this KeyNav
14292          */
14293         disable: function(){
14294                 if(!this.disabled){
14295                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14296                 this.el.un("keydown", this.relay);
14297             }else{
14298                 this.el.un("keydown", this.prepareEvent);
14299                 this.el.un("keypress", this.relay);
14300             }
14301                     this.disabled = true;
14302                 }
14303         }
14304 };/*
14305  * Based on:
14306  * Ext JS Library 1.1.1
14307  * Copyright(c) 2006-2007, Ext JS, LLC.
14308  *
14309  * Originally Released Under LGPL - original licence link has changed is not relivant.
14310  *
14311  * Fork - LGPL
14312  * <script type="text/javascript">
14313  */
14314
14315  
14316 /**
14317  * @class Roo.KeyMap
14318  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14319  * The constructor accepts the same config object as defined by {@link #addBinding}.
14320  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14321  * combination it will call the function with this signature (if the match is a multi-key
14322  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14323  * A KeyMap can also handle a string representation of keys.<br />
14324  * Usage:
14325  <pre><code>
14326 // map one key by key code
14327 var map = new Roo.KeyMap("my-element", {
14328     key: 13, // or Roo.EventObject.ENTER
14329     fn: myHandler,
14330     scope: myObject
14331 });
14332
14333 // map multiple keys to one action by string
14334 var map = new Roo.KeyMap("my-element", {
14335     key: "a\r\n\t",
14336     fn: myHandler,
14337     scope: myObject
14338 });
14339
14340 // map multiple keys to multiple actions by strings and array of codes
14341 var map = new Roo.KeyMap("my-element", [
14342     {
14343         key: [10,13],
14344         fn: function(){ alert("Return was pressed"); }
14345     }, {
14346         key: "abc",
14347         fn: function(){ alert('a, b or c was pressed'); }
14348     }, {
14349         key: "\t",
14350         ctrl:true,
14351         shift:true,
14352         fn: function(){ alert('Control + shift + tab was pressed.'); }
14353     }
14354 ]);
14355 </code></pre>
14356  * <b>Note: A KeyMap starts enabled</b>
14357  * @constructor
14358  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14359  * @param {Object} config The config (see {@link #addBinding})
14360  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14361  */
14362 Roo.KeyMap = function(el, config, eventName){
14363     this.el  = Roo.get(el);
14364     this.eventName = eventName || "keydown";
14365     this.bindings = [];
14366     if(config){
14367         this.addBinding(config);
14368     }
14369     this.enable();
14370 };
14371
14372 Roo.KeyMap.prototype = {
14373     /**
14374      * True to stop the event from bubbling and prevent the default browser action if the
14375      * key was handled by the KeyMap (defaults to false)
14376      * @type Boolean
14377      */
14378     stopEvent : false,
14379
14380     /**
14381      * Add a new binding to this KeyMap. The following config object properties are supported:
14382      * <pre>
14383 Property    Type             Description
14384 ----------  ---------------  ----------------------------------------------------------------------
14385 key         String/Array     A single keycode or an array of keycodes to handle
14386 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14387 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14388 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14389 fn          Function         The function to call when KeyMap finds the expected key combination
14390 scope       Object           The scope of the callback function
14391 </pre>
14392      *
14393      * Usage:
14394      * <pre><code>
14395 // Create a KeyMap
14396 var map = new Roo.KeyMap(document, {
14397     key: Roo.EventObject.ENTER,
14398     fn: handleKey,
14399     scope: this
14400 });
14401
14402 //Add a new binding to the existing KeyMap later
14403 map.addBinding({
14404     key: 'abc',
14405     shift: true,
14406     fn: handleKey,
14407     scope: this
14408 });
14409 </code></pre>
14410      * @param {Object/Array} config A single KeyMap config or an array of configs
14411      */
14412         addBinding : function(config){
14413         if(config instanceof Array){
14414             for(var i = 0, len = config.length; i < len; i++){
14415                 this.addBinding(config[i]);
14416             }
14417             return;
14418         }
14419         var keyCode = config.key,
14420             shift = config.shift, 
14421             ctrl = config.ctrl, 
14422             alt = config.alt,
14423             fn = config.fn,
14424             scope = config.scope;
14425         if(typeof keyCode == "string"){
14426             var ks = [];
14427             var keyString = keyCode.toUpperCase();
14428             for(var j = 0, len = keyString.length; j < len; j++){
14429                 ks.push(keyString.charCodeAt(j));
14430             }
14431             keyCode = ks;
14432         }
14433         var keyArray = keyCode instanceof Array;
14434         var handler = function(e){
14435             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14436                 var k = e.getKey();
14437                 if(keyArray){
14438                     for(var i = 0, len = keyCode.length; i < len; i++){
14439                         if(keyCode[i] == k){
14440                           if(this.stopEvent){
14441                               e.stopEvent();
14442                           }
14443                           fn.call(scope || window, k, e);
14444                           return;
14445                         }
14446                     }
14447                 }else{
14448                     if(k == keyCode){
14449                         if(this.stopEvent){
14450                            e.stopEvent();
14451                         }
14452                         fn.call(scope || window, k, e);
14453                     }
14454                 }
14455             }
14456         };
14457         this.bindings.push(handler);  
14458         },
14459
14460     /**
14461      * Shorthand for adding a single key listener
14462      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14463      * following options:
14464      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14465      * @param {Function} fn The function to call
14466      * @param {Object} scope (optional) The scope of the function
14467      */
14468     on : function(key, fn, scope){
14469         var keyCode, shift, ctrl, alt;
14470         if(typeof key == "object" && !(key instanceof Array)){
14471             keyCode = key.key;
14472             shift = key.shift;
14473             ctrl = key.ctrl;
14474             alt = key.alt;
14475         }else{
14476             keyCode = key;
14477         }
14478         this.addBinding({
14479             key: keyCode,
14480             shift: shift,
14481             ctrl: ctrl,
14482             alt: alt,
14483             fn: fn,
14484             scope: scope
14485         })
14486     },
14487
14488     // private
14489     handleKeyDown : function(e){
14490             if(this.enabled){ //just in case
14491             var b = this.bindings;
14492             for(var i = 0, len = b.length; i < len; i++){
14493                 b[i].call(this, e);
14494             }
14495             }
14496         },
14497         
14498         /**
14499          * Returns true if this KeyMap is enabled
14500          * @return {Boolean} 
14501          */
14502         isEnabled : function(){
14503             return this.enabled;  
14504         },
14505         
14506         /**
14507          * Enables this KeyMap
14508          */
14509         enable: function(){
14510                 if(!this.enabled){
14511                     this.el.on(this.eventName, this.handleKeyDown, this);
14512                     this.enabled = true;
14513                 }
14514         },
14515
14516         /**
14517          * Disable this KeyMap
14518          */
14519         disable: function(){
14520                 if(this.enabled){
14521                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14522                     this.enabled = false;
14523                 }
14524         }
14525 };/*
14526  * Based on:
14527  * Ext JS Library 1.1.1
14528  * Copyright(c) 2006-2007, Ext JS, LLC.
14529  *
14530  * Originally Released Under LGPL - original licence link has changed is not relivant.
14531  *
14532  * Fork - LGPL
14533  * <script type="text/javascript">
14534  */
14535
14536  
14537 /**
14538  * @class Roo.util.TextMetrics
14539  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14540  * wide, in pixels, a given block of text will be.
14541  * @singleton
14542  */
14543 Roo.util.TextMetrics = function(){
14544     var shared;
14545     return {
14546         /**
14547          * Measures the size of the specified text
14548          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14549          * that can affect the size of the rendered text
14550          * @param {String} text The text to measure
14551          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14552          * in order to accurately measure the text height
14553          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14554          */
14555         measure : function(el, text, fixedWidth){
14556             if(!shared){
14557                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14558             }
14559             shared.bind(el);
14560             shared.setFixedWidth(fixedWidth || 'auto');
14561             return shared.getSize(text);
14562         },
14563
14564         /**
14565          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14566          * the overhead of multiple calls to initialize the style properties on each measurement.
14567          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14568          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14569          * in order to accurately measure the text height
14570          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14571          */
14572         createInstance : function(el, fixedWidth){
14573             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14574         }
14575     };
14576 }();
14577
14578  
14579
14580 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14581     var ml = new Roo.Element(document.createElement('div'));
14582     document.body.appendChild(ml.dom);
14583     ml.position('absolute');
14584     ml.setLeftTop(-1000, -1000);
14585     ml.hide();
14586
14587     if(fixedWidth){
14588         ml.setWidth(fixedWidth);
14589     }
14590      
14591     var instance = {
14592         /**
14593          * Returns the size of the specified text based on the internal element's style and width properties
14594          * @memberOf Roo.util.TextMetrics.Instance#
14595          * @param {String} text The text to measure
14596          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14597          */
14598         getSize : function(text){
14599             ml.update(text);
14600             var s = ml.getSize();
14601             ml.update('');
14602             return s;
14603         },
14604
14605         /**
14606          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14607          * that can affect the size of the rendered text
14608          * @memberOf Roo.util.TextMetrics.Instance#
14609          * @param {String/HTMLElement} el The element, dom node or id
14610          */
14611         bind : function(el){
14612             ml.setStyle(
14613                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14614             );
14615         },
14616
14617         /**
14618          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14619          * to set a fixed width in order to accurately measure the text height.
14620          * @memberOf Roo.util.TextMetrics.Instance#
14621          * @param {Number} width The width to set on the element
14622          */
14623         setFixedWidth : function(width){
14624             ml.setWidth(width);
14625         },
14626
14627         /**
14628          * Returns the measured width of the specified text
14629          * @memberOf Roo.util.TextMetrics.Instance#
14630          * @param {String} text The text to measure
14631          * @return {Number} width The width in pixels
14632          */
14633         getWidth : function(text){
14634             ml.dom.style.width = 'auto';
14635             return this.getSize(text).width;
14636         },
14637
14638         /**
14639          * Returns the measured height of the specified text.  For multiline text, be sure to call
14640          * {@link #setFixedWidth} if necessary.
14641          * @memberOf Roo.util.TextMetrics.Instance#
14642          * @param {String} text The text to measure
14643          * @return {Number} height The height in pixels
14644          */
14645         getHeight : function(text){
14646             return this.getSize(text).height;
14647         }
14648     };
14649
14650     instance.bind(bindTo);
14651
14652     return instance;
14653 };
14654
14655 // backwards compat
14656 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14657  * Based on:
14658  * Ext JS Library 1.1.1
14659  * Copyright(c) 2006-2007, Ext JS, LLC.
14660  *
14661  * Originally Released Under LGPL - original licence link has changed is not relivant.
14662  *
14663  * Fork - LGPL
14664  * <script type="text/javascript">
14665  */
14666
14667 /**
14668  * @class Roo.state.Provider
14669  * Abstract base class for state provider implementations. This class provides methods
14670  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14671  * Provider interface.
14672  */
14673 Roo.state.Provider = function(){
14674     /**
14675      * @event statechange
14676      * Fires when a state change occurs.
14677      * @param {Provider} this This state provider
14678      * @param {String} key The state key which was changed
14679      * @param {String} value The encoded value for the state
14680      */
14681     this.addEvents({
14682         "statechange": true
14683     });
14684     this.state = {};
14685     Roo.state.Provider.superclass.constructor.call(this);
14686 };
14687 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14688     /**
14689      * Returns the current value for a key
14690      * @param {String} name The key name
14691      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14692      * @return {Mixed} The state data
14693      */
14694     get : function(name, defaultValue){
14695         return typeof this.state[name] == "undefined" ?
14696             defaultValue : this.state[name];
14697     },
14698     
14699     /**
14700      * Clears a value from the state
14701      * @param {String} name The key name
14702      */
14703     clear : function(name){
14704         delete this.state[name];
14705         this.fireEvent("statechange", this, name, null);
14706     },
14707     
14708     /**
14709      * Sets the value for a key
14710      * @param {String} name The key name
14711      * @param {Mixed} value The value to set
14712      */
14713     set : function(name, value){
14714         this.state[name] = value;
14715         this.fireEvent("statechange", this, name, value);
14716     },
14717     
14718     /**
14719      * Decodes a string previously encoded with {@link #encodeValue}.
14720      * @param {String} value The value to decode
14721      * @return {Mixed} The decoded value
14722      */
14723     decodeValue : function(cookie){
14724         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14725         var matches = re.exec(unescape(cookie));
14726         if(!matches || !matches[1]) return; // non state cookie
14727         var type = matches[1];
14728         var v = matches[2];
14729         switch(type){
14730             case "n":
14731                 return parseFloat(v);
14732             case "d":
14733                 return new Date(Date.parse(v));
14734             case "b":
14735                 return (v == "1");
14736             case "a":
14737                 var all = [];
14738                 var values = v.split("^");
14739                 for(var i = 0, len = values.length; i < len; i++){
14740                     all.push(this.decodeValue(values[i]));
14741                 }
14742                 return all;
14743            case "o":
14744                 var all = {};
14745                 var values = v.split("^");
14746                 for(var i = 0, len = values.length; i < len; i++){
14747                     var kv = values[i].split("=");
14748                     all[kv[0]] = this.decodeValue(kv[1]);
14749                 }
14750                 return all;
14751            default:
14752                 return v;
14753         }
14754     },
14755     
14756     /**
14757      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14758      * @param {Mixed} value The value to encode
14759      * @return {String} The encoded value
14760      */
14761     encodeValue : function(v){
14762         var enc;
14763         if(typeof v == "number"){
14764             enc = "n:" + v;
14765         }else if(typeof v == "boolean"){
14766             enc = "b:" + (v ? "1" : "0");
14767         }else if(v instanceof Date){
14768             enc = "d:" + v.toGMTString();
14769         }else if(v instanceof Array){
14770             var flat = "";
14771             for(var i = 0, len = v.length; i < len; i++){
14772                 flat += this.encodeValue(v[i]);
14773                 if(i != len-1) flat += "^";
14774             }
14775             enc = "a:" + flat;
14776         }else if(typeof v == "object"){
14777             var flat = "";
14778             for(var key in v){
14779                 if(typeof v[key] != "function"){
14780                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14781                 }
14782             }
14783             enc = "o:" + flat.substring(0, flat.length-1);
14784         }else{
14785             enc = "s:" + v;
14786         }
14787         return escape(enc);        
14788     }
14789 });
14790
14791 /*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801 /**
14802  * @class Roo.state.Manager
14803  * This is the global state manager. By default all components that are "state aware" check this class
14804  * for state information if you don't pass them a custom state provider. In order for this class
14805  * to be useful, it must be initialized with a provider when your application initializes.
14806  <pre><code>
14807 // in your initialization function
14808 init : function(){
14809    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14810    ...
14811    // supposed you have a {@link Roo.BorderLayout}
14812    var layout = new Roo.BorderLayout(...);
14813    layout.restoreState();
14814    // or a {Roo.BasicDialog}
14815    var dialog = new Roo.BasicDialog(...);
14816    dialog.restoreState();
14817  </code></pre>
14818  * @singleton
14819  */
14820 Roo.state.Manager = function(){
14821     var provider = new Roo.state.Provider();
14822     
14823     return {
14824         /**
14825          * Configures the default state provider for your application
14826          * @param {Provider} stateProvider The state provider to set
14827          */
14828         setProvider : function(stateProvider){
14829             provider = stateProvider;
14830         },
14831         
14832         /**
14833          * Returns the current value for a key
14834          * @param {String} name The key name
14835          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14836          * @return {Mixed} The state data
14837          */
14838         get : function(key, defaultValue){
14839             return provider.get(key, defaultValue);
14840         },
14841         
14842         /**
14843          * Sets the value for a key
14844          * @param {String} name The key name
14845          * @param {Mixed} value The state data
14846          */
14847          set : function(key, value){
14848             provider.set(key, value);
14849         },
14850         
14851         /**
14852          * Clears a value from the state
14853          * @param {String} name The key name
14854          */
14855         clear : function(key){
14856             provider.clear(key);
14857         },
14858         
14859         /**
14860          * Gets the currently configured state provider
14861          * @return {Provider} The state provider
14862          */
14863         getProvider : function(){
14864             return provider;
14865         }
14866     };
14867 }();
14868 /*
14869  * Based on:
14870  * Ext JS Library 1.1.1
14871  * Copyright(c) 2006-2007, Ext JS, LLC.
14872  *
14873  * Originally Released Under LGPL - original licence link has changed is not relivant.
14874  *
14875  * Fork - LGPL
14876  * <script type="text/javascript">
14877  */
14878 /**
14879  * @class Roo.state.CookieProvider
14880  * @extends Roo.state.Provider
14881  * The default Provider implementation which saves state via cookies.
14882  * <br />Usage:
14883  <pre><code>
14884    var cp = new Roo.state.CookieProvider({
14885        path: "/cgi-bin/",
14886        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14887        domain: "roojs.com"
14888    })
14889    Roo.state.Manager.setProvider(cp);
14890  </code></pre>
14891  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14892  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14893  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14894  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14895  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14896  * domain the page is running on including the 'www' like 'www.roojs.com')
14897  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14898  * @constructor
14899  * Create a new CookieProvider
14900  * @param {Object} config The configuration object
14901  */
14902 Roo.state.CookieProvider = function(config){
14903     Roo.state.CookieProvider.superclass.constructor.call(this);
14904     this.path = "/";
14905     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14906     this.domain = null;
14907     this.secure = false;
14908     Roo.apply(this, config);
14909     this.state = this.readCookies();
14910 };
14911
14912 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14913     // private
14914     set : function(name, value){
14915         if(typeof value == "undefined" || value === null){
14916             this.clear(name);
14917             return;
14918         }
14919         this.setCookie(name, value);
14920         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14921     },
14922
14923     // private
14924     clear : function(name){
14925         this.clearCookie(name);
14926         Roo.state.CookieProvider.superclass.clear.call(this, name);
14927     },
14928
14929     // private
14930     readCookies : function(){
14931         var cookies = {};
14932         var c = document.cookie + ";";
14933         var re = /\s?(.*?)=(.*?);/g;
14934         var matches;
14935         while((matches = re.exec(c)) != null){
14936             var name = matches[1];
14937             var value = matches[2];
14938             if(name && name.substring(0,3) == "ys-"){
14939                 cookies[name.substr(3)] = this.decodeValue(value);
14940             }
14941         }
14942         return cookies;
14943     },
14944
14945     // private
14946     setCookie : function(name, value){
14947         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14948            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14949            ((this.path == null) ? "" : ("; path=" + this.path)) +
14950            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14951            ((this.secure == true) ? "; secure" : "");
14952     },
14953
14954     // private
14955     clearCookie : function(name){
14956         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14957            ((this.path == null) ? "" : ("; path=" + this.path)) +
14958            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14959            ((this.secure == true) ? "; secure" : "");
14960     }
14961 });/*
14962  * Based on:
14963  * Ext JS Library 1.1.1
14964  * Copyright(c) 2006-2007, Ext JS, LLC.
14965  *
14966  * Originally Released Under LGPL - original licence link has changed is not relivant.
14967  *
14968  * Fork - LGPL
14969  * <script type="text/javascript">
14970  */
14971  
14972
14973 /**
14974  * @class Roo.ComponentMgr
14975  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14976  * @singleton
14977  */
14978 Roo.ComponentMgr = function(){
14979     var all = new Roo.util.MixedCollection();
14980
14981     return {
14982         /**
14983          * Registers a component.
14984          * @param {Roo.Component} c The component
14985          */
14986         register : function(c){
14987             all.add(c);
14988         },
14989
14990         /**
14991          * Unregisters a component.
14992          * @param {Roo.Component} c The component
14993          */
14994         unregister : function(c){
14995             all.remove(c);
14996         },
14997
14998         /**
14999          * Returns a component by id
15000          * @param {String} id The component id
15001          */
15002         get : function(id){
15003             return all.get(id);
15004         },
15005
15006         /**
15007          * Registers a function that will be called when a specified component is added to ComponentMgr
15008          * @param {String} id The component id
15009          * @param {Funtction} fn The callback function
15010          * @param {Object} scope The scope of the callback
15011          */
15012         onAvailable : function(id, fn, scope){
15013             all.on("add", function(index, o){
15014                 if(o.id == id){
15015                     fn.call(scope || o, o);
15016                     all.un("add", fn, scope);
15017                 }
15018             });
15019         }
15020     };
15021 }();/*
15022  * Based on:
15023  * Ext JS Library 1.1.1
15024  * Copyright(c) 2006-2007, Ext JS, LLC.
15025  *
15026  * Originally Released Under LGPL - original licence link has changed is not relivant.
15027  *
15028  * Fork - LGPL
15029  * <script type="text/javascript">
15030  */
15031  
15032 /**
15033  * @class Roo.Component
15034  * @extends Roo.util.Observable
15035  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15036  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15037  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15038  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15039  * All visual components (widgets) that require rendering into a layout should subclass Component.
15040  * @constructor
15041  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15042  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15043  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15044  */
15045 Roo.Component = function(config){
15046     config = config || {};
15047     if(config.tagName || config.dom || typeof config == "string"){ // element object
15048         config = {el: config, id: config.id || config};
15049     }
15050     this.initialConfig = config;
15051
15052     Roo.apply(this, config);
15053     this.addEvents({
15054         /**
15055          * @event disable
15056          * Fires after the component is disabled.
15057              * @param {Roo.Component} this
15058              */
15059         disable : true,
15060         /**
15061          * @event enable
15062          * Fires after the component is enabled.
15063              * @param {Roo.Component} this
15064              */
15065         enable : true,
15066         /**
15067          * @event beforeshow
15068          * Fires before the component is shown.  Return false to stop the show.
15069              * @param {Roo.Component} this
15070              */
15071         beforeshow : true,
15072         /**
15073          * @event show
15074          * Fires after the component is shown.
15075              * @param {Roo.Component} this
15076              */
15077         show : true,
15078         /**
15079          * @event beforehide
15080          * Fires before the component is hidden. Return false to stop the hide.
15081              * @param {Roo.Component} this
15082              */
15083         beforehide : true,
15084         /**
15085          * @event hide
15086          * Fires after the component is hidden.
15087              * @param {Roo.Component} this
15088              */
15089         hide : true,
15090         /**
15091          * @event beforerender
15092          * Fires before the component is rendered. Return false to stop the render.
15093              * @param {Roo.Component} this
15094              */
15095         beforerender : true,
15096         /**
15097          * @event render
15098          * Fires after the component is rendered.
15099              * @param {Roo.Component} this
15100              */
15101         render : true,
15102         /**
15103          * @event beforedestroy
15104          * Fires before the component is destroyed. Return false to stop the destroy.
15105              * @param {Roo.Component} this
15106              */
15107         beforedestroy : true,
15108         /**
15109          * @event destroy
15110          * Fires after the component is destroyed.
15111              * @param {Roo.Component} this
15112              */
15113         destroy : true
15114     });
15115     if(!this.id){
15116         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15117     }
15118     Roo.ComponentMgr.register(this);
15119     Roo.Component.superclass.constructor.call(this);
15120     this.initComponent();
15121     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15122         this.render(this.renderTo);
15123         delete this.renderTo;
15124     }
15125 };
15126
15127 /** @private */
15128 Roo.Component.AUTO_ID = 1000;
15129
15130 Roo.extend(Roo.Component, Roo.util.Observable, {
15131     /**
15132      * @scope Roo.Component.prototype
15133      * @type {Boolean}
15134      * true if this component is hidden. Read-only.
15135      */
15136     hidden : false,
15137     /**
15138      * @type {Boolean}
15139      * true if this component is disabled. Read-only.
15140      */
15141     disabled : false,
15142     /**
15143      * @type {Boolean}
15144      * true if this component has been rendered. Read-only.
15145      */
15146     rendered : false,
15147     
15148     /** @cfg {String} disableClass
15149      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15150      */
15151     disabledClass : "x-item-disabled",
15152         /** @cfg {Boolean} allowDomMove
15153          * Whether the component can move the Dom node when rendering (defaults to true).
15154          */
15155     allowDomMove : true,
15156     /** @cfg {String} hideMode
15157      * How this component should hidden. Supported values are
15158      * "visibility" (css visibility), "offsets" (negative offset position) and
15159      * "display" (css display) - defaults to "display".
15160      */
15161     hideMode: 'display',
15162
15163     /** @private */
15164     ctype : "Roo.Component",
15165
15166     /**
15167      * @cfg {String} actionMode 
15168      * which property holds the element that used for  hide() / show() / disable() / enable()
15169      * default is 'el' 
15170      */
15171     actionMode : "el",
15172
15173     /** @private */
15174     getActionEl : function(){
15175         return this[this.actionMode];
15176     },
15177
15178     initComponent : Roo.emptyFn,
15179     /**
15180      * If this is a lazy rendering component, render it to its container element.
15181      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15182      */
15183     render : function(container, position){
15184         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15185             if(!container && this.el){
15186                 this.el = Roo.get(this.el);
15187                 container = this.el.dom.parentNode;
15188                 this.allowDomMove = false;
15189             }
15190             this.container = Roo.get(container);
15191             this.rendered = true;
15192             if(position !== undefined){
15193                 if(typeof position == 'number'){
15194                     position = this.container.dom.childNodes[position];
15195                 }else{
15196                     position = Roo.getDom(position);
15197                 }
15198             }
15199             this.onRender(this.container, position || null);
15200             if(this.cls){
15201                 this.el.addClass(this.cls);
15202                 delete this.cls;
15203             }
15204             if(this.style){
15205                 this.el.applyStyles(this.style);
15206                 delete this.style;
15207             }
15208             this.fireEvent("render", this);
15209             this.afterRender(this.container);
15210             if(this.hidden){
15211                 this.hide();
15212             }
15213             if(this.disabled){
15214                 this.disable();
15215             }
15216         }
15217         return this;
15218     },
15219
15220     /** @private */
15221     // default function is not really useful
15222     onRender : function(ct, position){
15223         if(this.el){
15224             this.el = Roo.get(this.el);
15225             if(this.allowDomMove !== false){
15226                 ct.dom.insertBefore(this.el.dom, position);
15227             }
15228         }
15229     },
15230
15231     /** @private */
15232     getAutoCreate : function(){
15233         var cfg = typeof this.autoCreate == "object" ?
15234                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15235         if(this.id && !cfg.id){
15236             cfg.id = this.id;
15237         }
15238         return cfg;
15239     },
15240
15241     /** @private */
15242     afterRender : Roo.emptyFn,
15243
15244     /**
15245      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15246      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15247      */
15248     destroy : function(){
15249         if(this.fireEvent("beforedestroy", this) !== false){
15250             this.purgeListeners();
15251             this.beforeDestroy();
15252             if(this.rendered){
15253                 this.el.removeAllListeners();
15254                 this.el.remove();
15255                 if(this.actionMode == "container"){
15256                     this.container.remove();
15257                 }
15258             }
15259             this.onDestroy();
15260             Roo.ComponentMgr.unregister(this);
15261             this.fireEvent("destroy", this);
15262         }
15263     },
15264
15265         /** @private */
15266     beforeDestroy : function(){
15267
15268     },
15269
15270         /** @private */
15271         onDestroy : function(){
15272
15273     },
15274
15275     /**
15276      * Returns the underlying {@link Roo.Element}.
15277      * @return {Roo.Element} The element
15278      */
15279     getEl : function(){
15280         return this.el;
15281     },
15282
15283     /**
15284      * Returns the id of this component.
15285      * @return {String}
15286      */
15287     getId : function(){
15288         return this.id;
15289     },
15290
15291     /**
15292      * Try to focus this component.
15293      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15294      * @return {Roo.Component} this
15295      */
15296     focus : function(selectText){
15297         if(this.rendered){
15298             this.el.focus();
15299             if(selectText === true){
15300                 this.el.dom.select();
15301             }
15302         }
15303         return this;
15304     },
15305
15306     /** @private */
15307     blur : function(){
15308         if(this.rendered){
15309             this.el.blur();
15310         }
15311         return this;
15312     },
15313
15314     /**
15315      * Disable this component.
15316      * @return {Roo.Component} this
15317      */
15318     disable : function(){
15319         if(this.rendered){
15320             this.onDisable();
15321         }
15322         this.disabled = true;
15323         this.fireEvent("disable", this);
15324         return this;
15325     },
15326
15327         // private
15328     onDisable : function(){
15329         this.getActionEl().addClass(this.disabledClass);
15330         this.el.dom.disabled = true;
15331     },
15332
15333     /**
15334      * Enable this component.
15335      * @return {Roo.Component} this
15336      */
15337     enable : function(){
15338         if(this.rendered){
15339             this.onEnable();
15340         }
15341         this.disabled = false;
15342         this.fireEvent("enable", this);
15343         return this;
15344     },
15345
15346         // private
15347     onEnable : function(){
15348         this.getActionEl().removeClass(this.disabledClass);
15349         this.el.dom.disabled = false;
15350     },
15351
15352     /**
15353      * Convenience function for setting disabled/enabled by boolean.
15354      * @param {Boolean} disabled
15355      */
15356     setDisabled : function(disabled){
15357         this[disabled ? "disable" : "enable"]();
15358     },
15359
15360     /**
15361      * Show this component.
15362      * @return {Roo.Component} this
15363      */
15364     show: function(){
15365         if(this.fireEvent("beforeshow", this) !== false){
15366             this.hidden = false;
15367             if(this.rendered){
15368                 this.onShow();
15369             }
15370             this.fireEvent("show", this);
15371         }
15372         return this;
15373     },
15374
15375     // private
15376     onShow : function(){
15377         var ae = this.getActionEl();
15378         if(this.hideMode == 'visibility'){
15379             ae.dom.style.visibility = "visible";
15380         }else if(this.hideMode == 'offsets'){
15381             ae.removeClass('x-hidden');
15382         }else{
15383             ae.dom.style.display = "";
15384         }
15385     },
15386
15387     /**
15388      * Hide this component.
15389      * @return {Roo.Component} this
15390      */
15391     hide: function(){
15392         if(this.fireEvent("beforehide", this) !== false){
15393             this.hidden = true;
15394             if(this.rendered){
15395                 this.onHide();
15396             }
15397             this.fireEvent("hide", this);
15398         }
15399         return this;
15400     },
15401
15402     // private
15403     onHide : function(){
15404         var ae = this.getActionEl();
15405         if(this.hideMode == 'visibility'){
15406             ae.dom.style.visibility = "hidden";
15407         }else if(this.hideMode == 'offsets'){
15408             ae.addClass('x-hidden');
15409         }else{
15410             ae.dom.style.display = "none";
15411         }
15412     },
15413
15414     /**
15415      * Convenience function to hide or show this component by boolean.
15416      * @param {Boolean} visible True to show, false to hide
15417      * @return {Roo.Component} this
15418      */
15419     setVisible: function(visible){
15420         if(visible) {
15421             this.show();
15422         }else{
15423             this.hide();
15424         }
15425         return this;
15426     },
15427
15428     /**
15429      * Returns true if this component is visible.
15430      */
15431     isVisible : function(){
15432         return this.getActionEl().isVisible();
15433     },
15434
15435     cloneConfig : function(overrides){
15436         overrides = overrides || {};
15437         var id = overrides.id || Roo.id();
15438         var cfg = Roo.applyIf(overrides, this.initialConfig);
15439         cfg.id = id; // prevent dup id
15440         return new this.constructor(cfg);
15441     }
15442 });/*
15443  * Based on:
15444  * Ext JS Library 1.1.1
15445  * Copyright(c) 2006-2007, Ext JS, LLC.
15446  *
15447  * Originally Released Under LGPL - original licence link has changed is not relivant.
15448  *
15449  * Fork - LGPL
15450  * <script type="text/javascript">
15451  */
15452
15453 /**
15454  * @class Roo.BoxComponent
15455  * @extends Roo.Component
15456  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15457  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15458  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15459  * layout containers.
15460  * @constructor
15461  * @param {Roo.Element/String/Object} config The configuration options.
15462  */
15463 Roo.BoxComponent = function(config){
15464     Roo.Component.call(this, config);
15465     this.addEvents({
15466         /**
15467          * @event resize
15468          * Fires after the component is resized.
15469              * @param {Roo.Component} this
15470              * @param {Number} adjWidth The box-adjusted width that was set
15471              * @param {Number} adjHeight The box-adjusted height that was set
15472              * @param {Number} rawWidth The width that was originally specified
15473              * @param {Number} rawHeight The height that was originally specified
15474              */
15475         resize : true,
15476         /**
15477          * @event move
15478          * Fires after the component is moved.
15479              * @param {Roo.Component} this
15480              * @param {Number} x The new x position
15481              * @param {Number} y The new y position
15482              */
15483         move : true
15484     });
15485 };
15486
15487 Roo.extend(Roo.BoxComponent, Roo.Component, {
15488     // private, set in afterRender to signify that the component has been rendered
15489     boxReady : false,
15490     // private, used to defer height settings to subclasses
15491     deferHeight: false,
15492     /** @cfg {Number} width
15493      * width (optional) size of component
15494      */
15495      /** @cfg {Number} height
15496      * height (optional) size of component
15497      */
15498      
15499     /**
15500      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15501      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15502      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15503      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15504      * @return {Roo.BoxComponent} this
15505      */
15506     setSize : function(w, h){
15507         // support for standard size objects
15508         if(typeof w == 'object'){
15509             h = w.height;
15510             w = w.width;
15511         }
15512         // not rendered
15513         if(!this.boxReady){
15514             this.width = w;
15515             this.height = h;
15516             return this;
15517         }
15518
15519         // prevent recalcs when not needed
15520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15521             return this;
15522         }
15523         this.lastSize = {width: w, height: h};
15524
15525         var adj = this.adjustSize(w, h);
15526         var aw = adj.width, ah = adj.height;
15527         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15528             var rz = this.getResizeEl();
15529             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15530                 rz.setSize(aw, ah);
15531             }else if(!this.deferHeight && ah !== undefined){
15532                 rz.setHeight(ah);
15533             }else if(aw !== undefined){
15534                 rz.setWidth(aw);
15535             }
15536             this.onResize(aw, ah, w, h);
15537             this.fireEvent('resize', this, aw, ah, w, h);
15538         }
15539         return this;
15540     },
15541
15542     /**
15543      * Gets the current size of the component's underlying element.
15544      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15545      */
15546     getSize : function(){
15547         return this.el.getSize();
15548     },
15549
15550     /**
15551      * Gets the current XY position of the component's underlying element.
15552      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15553      * @return {Array} The XY position of the element (e.g., [100, 200])
15554      */
15555     getPosition : function(local){
15556         if(local === true){
15557             return [this.el.getLeft(true), this.el.getTop(true)];
15558         }
15559         return this.xy || this.el.getXY();
15560     },
15561
15562     /**
15563      * Gets the current box measurements of the component's underlying element.
15564      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15565      * @returns {Object} box An object in the format {x, y, width, height}
15566      */
15567     getBox : function(local){
15568         var s = this.el.getSize();
15569         if(local){
15570             s.x = this.el.getLeft(true);
15571             s.y = this.el.getTop(true);
15572         }else{
15573             var xy = this.xy || this.el.getXY();
15574             s.x = xy[0];
15575             s.y = xy[1];
15576         }
15577         return s;
15578     },
15579
15580     /**
15581      * Sets the current box measurements of the component's underlying element.
15582      * @param {Object} box An object in the format {x, y, width, height}
15583      * @returns {Roo.BoxComponent} this
15584      */
15585     updateBox : function(box){
15586         this.setSize(box.width, box.height);
15587         this.setPagePosition(box.x, box.y);
15588         return this;
15589     },
15590
15591     // protected
15592     getResizeEl : function(){
15593         return this.resizeEl || this.el;
15594     },
15595
15596     // protected
15597     getPositionEl : function(){
15598         return this.positionEl || this.el;
15599     },
15600
15601     /**
15602      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15603      * This method fires the move event.
15604      * @param {Number} left The new left
15605      * @param {Number} top The new top
15606      * @returns {Roo.BoxComponent} this
15607      */
15608     setPosition : function(x, y){
15609         this.x = x;
15610         this.y = y;
15611         if(!this.boxReady){
15612             return this;
15613         }
15614         var adj = this.adjustPosition(x, y);
15615         var ax = adj.x, ay = adj.y;
15616
15617         var el = this.getPositionEl();
15618         if(ax !== undefined || ay !== undefined){
15619             if(ax !== undefined && ay !== undefined){
15620                 el.setLeftTop(ax, ay);
15621             }else if(ax !== undefined){
15622                 el.setLeft(ax);
15623             }else if(ay !== undefined){
15624                 el.setTop(ay);
15625             }
15626             this.onPosition(ax, ay);
15627             this.fireEvent('move', this, ax, ay);
15628         }
15629         return this;
15630     },
15631
15632     /**
15633      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15634      * This method fires the move event.
15635      * @param {Number} x The new x position
15636      * @param {Number} y The new y position
15637      * @returns {Roo.BoxComponent} this
15638      */
15639     setPagePosition : function(x, y){
15640         this.pageX = x;
15641         this.pageY = y;
15642         if(!this.boxReady){
15643             return;
15644         }
15645         if(x === undefined || y === undefined){ // cannot translate undefined points
15646             return;
15647         }
15648         var p = this.el.translatePoints(x, y);
15649         this.setPosition(p.left, p.top);
15650         return this;
15651     },
15652
15653     // private
15654     onRender : function(ct, position){
15655         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15656         if(this.resizeEl){
15657             this.resizeEl = Roo.get(this.resizeEl);
15658         }
15659         if(this.positionEl){
15660             this.positionEl = Roo.get(this.positionEl);
15661         }
15662     },
15663
15664     // private
15665     afterRender : function(){
15666         Roo.BoxComponent.superclass.afterRender.call(this);
15667         this.boxReady = true;
15668         this.setSize(this.width, this.height);
15669         if(this.x || this.y){
15670             this.setPosition(this.x, this.y);
15671         }
15672         if(this.pageX || this.pageY){
15673             this.setPagePosition(this.pageX, this.pageY);
15674         }
15675     },
15676
15677     /**
15678      * Force the component's size to recalculate based on the underlying element's current height and width.
15679      * @returns {Roo.BoxComponent} this
15680      */
15681     syncSize : function(){
15682         delete this.lastSize;
15683         this.setSize(this.el.getWidth(), this.el.getHeight());
15684         return this;
15685     },
15686
15687     /**
15688      * Called after the component is resized, this method is empty by default but can be implemented by any
15689      * subclass that needs to perform custom logic after a resize occurs.
15690      * @param {Number} adjWidth The box-adjusted width that was set
15691      * @param {Number} adjHeight The box-adjusted height that was set
15692      * @param {Number} rawWidth The width that was originally specified
15693      * @param {Number} rawHeight The height that was originally specified
15694      */
15695     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15696
15697     },
15698
15699     /**
15700      * Called after the component is moved, this method is empty by default but can be implemented by any
15701      * subclass that needs to perform custom logic after a move occurs.
15702      * @param {Number} x The new x position
15703      * @param {Number} y The new y position
15704      */
15705     onPosition : function(x, y){
15706
15707     },
15708
15709     // private
15710     adjustSize : function(w, h){
15711         if(this.autoWidth){
15712             w = 'auto';
15713         }
15714         if(this.autoHeight){
15715             h = 'auto';
15716         }
15717         return {width : w, height: h};
15718     },
15719
15720     // private
15721     adjustPosition : function(x, y){
15722         return {x : x, y: y};
15723     }
15724 });/*
15725  * Original code for Roojs - LGPL
15726  * <script type="text/javascript">
15727  */
15728  
15729 /**
15730  * @class Roo.XComponent
15731  * A delayed Element creator...
15732  * Or a way to group chunks of interface together.
15733  * 
15734  * Mypart.xyx = new Roo.XComponent({
15735
15736     parent : 'Mypart.xyz', // empty == document.element.!!
15737     order : '001',
15738     name : 'xxxx'
15739     region : 'xxxx'
15740     disabled : function() {} 
15741      
15742     tree : function() { // return an tree of xtype declared components
15743         var MODULE = this;
15744         return 
15745         {
15746             xtype : 'NestedLayoutPanel',
15747             // technicall
15748         }
15749      ]
15750  *})
15751  *
15752  *
15753  * It can be used to build a big heiracy, with parent etc.
15754  * or you can just use this to render a single compoent to a dom element
15755  * MYPART.render(Roo.Element | String(id) | dom_element )
15756  * 
15757  * @extends Roo.util.Observable
15758  * @constructor
15759  * @param cfg {Object} configuration of component
15760  * 
15761  */
15762 Roo.XComponent = function(cfg) {
15763     Roo.apply(this, cfg);
15764     this.addEvents({ 
15765         /**
15766              * @event built
15767              * Fires when this the componnt is built
15768              * @param {Roo.XComponent} c the component
15769              */
15770         'built' : true
15771         
15772     });
15773     this.region = this.region || 'center'; // default..
15774     Roo.XComponent.register(this);
15775     this.modules = false;
15776     this.el = false; // where the layout goes..
15777     
15778     
15779 }
15780 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15781     /**
15782      * @property el
15783      * The created element (with Roo.factory())
15784      * @type {Roo.Layout}
15785      */
15786     el  : false,
15787     
15788     /**
15789      * @property el
15790      * for BC  - use el in new code
15791      * @type {Roo.Layout}
15792      */
15793     panel : false,
15794     
15795     /**
15796      * @property layout
15797      * for BC  - use el in new code
15798      * @type {Roo.Layout}
15799      */
15800     layout : false,
15801     
15802      /**
15803      * @cfg {Function|boolean} disabled
15804      * If this module is disabled by some rule, return true from the funtion
15805      */
15806     disabled : false,
15807     
15808     /**
15809      * @cfg {String} parent 
15810      * Name of parent element which it get xtype added to..
15811      */
15812     parent: false,
15813     
15814     /**
15815      * @cfg {String} order
15816      * Used to set the order in which elements are created (usefull for multiple tabs)
15817      */
15818     
15819     order : false,
15820     /**
15821      * @cfg {String} name
15822      * String to display while loading.
15823      */
15824     name : false,
15825     /**
15826      * @cfg {String} region
15827      * Region to render component to (defaults to center)
15828      */
15829     region : 'center',
15830     
15831     /**
15832      * @cfg {Array} items
15833      * A single item array - the first element is the root of the tree..
15834      * It's done this way to stay compatible with the Xtype system...
15835      */
15836     items : false,
15837     
15838     /**
15839      * @property _tree
15840      * The method that retuns the tree of parts that make up this compoennt 
15841      * @type {function}
15842      */
15843     _tree  : false,
15844     
15845      /**
15846      * render
15847      * render element to dom or tree
15848      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15849      */
15850     
15851     render : function(el)
15852     {
15853         
15854         el = el || false;
15855         var hp = this.parent ? 1 : 0;
15856         
15857         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15858             // if parent is a '#.....' string, then let's use that..
15859             var ename = this.parent.substr(1)
15860             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15861             el = Roo.get(ename);
15862             if (!el && !this.parent) {
15863                 Roo.log("Warning - element can not be found :#" + ename );
15864                 return;
15865             }
15866         }
15867         
15868         
15869         if (!this.parent) {
15870             
15871             el = el ? Roo.get(el) : false;      
15872             
15873             // it's a top level one..
15874             this.parent =  {
15875                 el : new Roo.BorderLayout(el || document.body, {
15876                 
15877                      center: {
15878                          titlebar: false,
15879                          autoScroll:false,
15880                          closeOnTab: true,
15881                          tabPosition: 'top',
15882                           //resizeTabs: true,
15883                          alwaysShowTabs: el && hp? false :  true,
15884                          hideTabs: el || !hp ? true :  false,
15885                          minTabWidth: 140
15886                      }
15887                  })
15888             }
15889         }
15890         
15891                 if (!this.parent.el) {
15892                         // probably an old style ctor, which has been disabled.
15893                         return;
15894                         
15895                 }
15896                 // The 'tree' method is  '_tree now' 
15897             
15898         var tree = this._tree ? this._tree() : this.tree();
15899         tree.region = tree.region || this.region;
15900         
15901         if (this.parent.el === true) {
15902             // bootstrap... - body..
15903             this.parent.el = Roo.factory(tree);
15904         }
15905         
15906         this.el = this.parent.el.addxtype(tree);
15907         this.fireEvent('built', this);
15908         
15909         this.panel = this.el;
15910         this.layout = this.panel.layout;
15911                 this.parentLayout = this.parent.layout  || false;  
15912          
15913     }
15914     
15915 });
15916
15917 Roo.apply(Roo.XComponent, {
15918     /**
15919      * @property  hideProgress
15920      * true to disable the building progress bar.. usefull on single page renders.
15921      * @type Boolean
15922      */
15923     hideProgress : false,
15924     /**
15925      * @property  buildCompleted
15926      * True when the builder has completed building the interface.
15927      * @type Boolean
15928      */
15929     buildCompleted : false,
15930      
15931     /**
15932      * @property  topModule
15933      * the upper most module - uses document.element as it's constructor.
15934      * @type Object
15935      */
15936      
15937     topModule  : false,
15938       
15939     /**
15940      * @property  modules
15941      * array of modules to be created by registration system.
15942      * @type {Array} of Roo.XComponent
15943      */
15944     
15945     modules : [],
15946     /**
15947      * @property  elmodules
15948      * array of modules to be created by which use #ID 
15949      * @type {Array} of Roo.XComponent
15950      */
15951      
15952     elmodules : [],
15953
15954      /**
15955      * @property  build_from_html
15956      * Build elements from html - used by bootstrap HTML stuff 
15957      *    - this is cleared after build is completed
15958      * @type {boolean} true  (default false)
15959      */
15960      
15961     build_from_html : false,
15962
15963     /**
15964      * Register components to be built later.
15965      *
15966      * This solves the following issues
15967      * - Building is not done on page load, but after an authentication process has occured.
15968      * - Interface elements are registered on page load
15969      * - Parent Interface elements may not be loaded before child, so this handles that..
15970      * 
15971      *
15972      * example:
15973      * 
15974      * MyApp.register({
15975           order : '000001',
15976           module : 'Pman.Tab.projectMgr',
15977           region : 'center',
15978           parent : 'Pman.layout',
15979           disabled : false,  // or use a function..
15980         })
15981      
15982      * * @param {Object} details about module
15983      */
15984     register : function(obj) {
15985                 
15986         Roo.XComponent.event.fireEvent('register', obj);
15987         switch(typeof(obj.disabled) ) {
15988                 
15989             case 'undefined':
15990                 break;
15991             
15992             case 'function':
15993                 if ( obj.disabled() ) {
15994                         return;
15995                 }
15996                 break;
15997             
15998             default:
15999                 if (obj.disabled) {
16000                         return;
16001                 }
16002                 break;
16003         }
16004                 
16005         this.modules.push(obj);
16006          
16007     },
16008     /**
16009      * convert a string to an object..
16010      * eg. 'AAA.BBB' -> finds AAA.BBB
16011
16012      */
16013     
16014     toObject : function(str)
16015     {
16016         if (!str || typeof(str) == 'object') {
16017             return str;
16018         }
16019         if (str.substring(0,1) == '#') {
16020             return str;
16021         }
16022
16023         var ar = str.split('.');
16024         var rt, o;
16025         rt = ar.shift();
16026             /** eval:var:o */
16027         try {
16028             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16029         } catch (e) {
16030             throw "Module not found : " + str;
16031         }
16032         
16033         if (o === false) {
16034             throw "Module not found : " + str;
16035         }
16036         Roo.each(ar, function(e) {
16037             if (typeof(o[e]) == 'undefined') {
16038                 throw "Module not found : " + str;
16039             }
16040             o = o[e];
16041         });
16042         
16043         return o;
16044         
16045     },
16046     
16047     
16048     /**
16049      * move modules into their correct place in the tree..
16050      * 
16051      */
16052     preBuild : function ()
16053     {
16054         var _t = this;
16055         Roo.each(this.modules , function (obj)
16056         {
16057             Roo.XComponent.event.fireEvent('beforebuild', obj);
16058             
16059             var opar = obj.parent;
16060             try { 
16061                 obj.parent = this.toObject(opar);
16062             } catch(e) {
16063                 Roo.log("parent:toObject failed: " + e.toString());
16064                 return;
16065             }
16066             
16067             if (!obj.parent) {
16068                 Roo.debug && Roo.log("GOT top level module");
16069                 Roo.debug && Roo.log(obj);
16070                 obj.modules = new Roo.util.MixedCollection(false, 
16071                     function(o) { return o.order + '' }
16072                 );
16073                 this.topModule = obj;
16074                 return;
16075             }
16076                         // parent is a string (usually a dom element name..)
16077             if (typeof(obj.parent) == 'string') {
16078                 this.elmodules.push(obj);
16079                 return;
16080             }
16081             if (obj.parent.constructor != Roo.XComponent) {
16082                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16083             }
16084             if (!obj.parent.modules) {
16085                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16086                     function(o) { return o.order + '' }
16087                 );
16088             }
16089             if (obj.parent.disabled) {
16090                 obj.disabled = true;
16091             }
16092             obj.parent.modules.add(obj);
16093         }, this);
16094     },
16095     
16096      /**
16097      * make a list of modules to build.
16098      * @return {Array} list of modules. 
16099      */ 
16100     
16101     buildOrder : function()
16102     {
16103         var _this = this;
16104         var cmp = function(a,b) {   
16105             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16106         };
16107         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16108             throw "No top level modules to build";
16109         }
16110         
16111         // make a flat list in order of modules to build.
16112         var mods = this.topModule ? [ this.topModule ] : [];
16113                 
16114         
16115         // elmodules (is a list of DOM based modules )
16116         Roo.each(this.elmodules, function(e) {
16117             mods.push(e);
16118             if (!this.topModule &&
16119                 typeof(e.parent) == 'string' &&
16120                 e.parent.substring(0,1) == '#' &&
16121                 Roo.get(e.parent.substr(1))
16122                ) {
16123                 
16124                 _this.topModule = e;
16125             }
16126             
16127         });
16128
16129         
16130         // add modules to their parents..
16131         var addMod = function(m) {
16132             Roo.debug && Roo.log("build Order: add: " + m.name);
16133                 
16134             mods.push(m);
16135             if (m.modules && !m.disabled) {
16136                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16137                 m.modules.keySort('ASC',  cmp );
16138                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16139     
16140                 m.modules.each(addMod);
16141             } else {
16142                 Roo.debug && Roo.log("build Order: no child modules");
16143             }
16144             // not sure if this is used any more..
16145             if (m.finalize) {
16146                 m.finalize.name = m.name + " (clean up) ";
16147                 mods.push(m.finalize);
16148             }
16149             
16150         }
16151         if (this.topModule && this.topModule.modules) { 
16152             this.topModule.modules.keySort('ASC',  cmp );
16153             this.topModule.modules.each(addMod);
16154         } 
16155         return mods;
16156     },
16157     
16158      /**
16159      * Build the registered modules.
16160      * @param {Object} parent element.
16161      * @param {Function} optional method to call after module has been added.
16162      * 
16163      */ 
16164    
16165     build : function(opts) 
16166     {
16167         
16168         if (typeof(opts) != 'undefined') {
16169             Roo.apply(this,opts);
16170         }
16171         
16172         this.preBuild();
16173         var mods = this.buildOrder();
16174       
16175         //this.allmods = mods;
16176         //Roo.debug && Roo.log(mods);
16177         //return;
16178         if (!mods.length) { // should not happen
16179             throw "NO modules!!!";
16180         }
16181         
16182         
16183         var msg = "Building Interface...";
16184         // flash it up as modal - so we store the mask!?
16185         if (!this.hideProgress && Roo.MessageBox) {
16186             Roo.MessageBox.show({ title: 'loading' });
16187             Roo.MessageBox.show({
16188                title: "Please wait...",
16189                msg: msg,
16190                width:450,
16191                progress:true,
16192                closable:false,
16193                modal: false
16194               
16195             });
16196         }
16197         var total = mods.length;
16198         
16199         var _this = this;
16200         var progressRun = function() {
16201             if (!mods.length) {
16202                 Roo.debug && Roo.log('hide?');
16203                 if (!this.hideProgress && Roo.MessageBox) {
16204                     Roo.MessageBox.hide();
16205                 }
16206                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16207                 
16208                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16209                 
16210                 // THE END...
16211                 return false;   
16212             }
16213             
16214             var m = mods.shift();
16215             
16216             
16217             Roo.debug && Roo.log(m);
16218             // not sure if this is supported any more.. - modules that are are just function
16219             if (typeof(m) == 'function') { 
16220                 m.call(this);
16221                 return progressRun.defer(10, _this);
16222             } 
16223             
16224             
16225             msg = "Building Interface " + (total  - mods.length) + 
16226                     " of " + total + 
16227                     (m.name ? (' - ' + m.name) : '');
16228                         Roo.debug && Roo.log(msg);
16229             if (!this.hideProgress &&  Roo.MessageBox) { 
16230                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16231             }
16232             
16233          
16234             // is the module disabled?
16235             var disabled = (typeof(m.disabled) == 'function') ?
16236                 m.disabled.call(m.module.disabled) : m.disabled;    
16237             
16238             
16239             if (disabled) {
16240                 return progressRun(); // we do not update the display!
16241             }
16242             
16243             // now build 
16244             
16245                         
16246                         
16247             m.render();
16248             // it's 10 on top level, and 1 on others??? why...
16249             return progressRun.defer(10, _this);
16250              
16251         }
16252         progressRun.defer(1, _this);
16253      
16254         
16255         
16256     },
16257         
16258         
16259         /**
16260          * Event Object.
16261          *
16262          *
16263          */
16264         event: false, 
16265     /**
16266          * wrapper for event.on - aliased later..  
16267          * Typically use to register a event handler for register:
16268          *
16269          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16270          *
16271          */
16272     on : false
16273    
16274     
16275     
16276 });
16277
16278 Roo.XComponent.event = new Roo.util.Observable({
16279                 events : { 
16280                         /**
16281                          * @event register
16282                          * Fires when an Component is registered,
16283                          * set the disable property on the Component to stop registration.
16284                          * @param {Roo.XComponent} c the component being registerd.
16285                          * 
16286                          */
16287                         'register' : true,
16288             /**
16289                          * @event beforebuild
16290                          * Fires before each Component is built
16291                          * can be used to apply permissions.
16292                          * @param {Roo.XComponent} c the component being registerd.
16293                          * 
16294                          */
16295                         'beforebuild' : true,
16296                         /**
16297                          * @event buildcomplete
16298                          * Fires on the top level element when all elements have been built
16299                          * @param {Roo.XComponent} the top level component.
16300                          */
16301                         'buildcomplete' : true
16302                         
16303                 }
16304 });
16305
16306 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16307  /*
16308  * Based on:
16309  * Ext JS Library 1.1.1
16310  * Copyright(c) 2006-2007, Ext JS, LLC.
16311  *
16312  * Originally Released Under LGPL - original licence link has changed is not relivant.
16313  *
16314  * Fork - LGPL
16315  * <script type="text/javascript">
16316  */
16317
16318
16319
16320 /*
16321  * These classes are derivatives of the similarly named classes in the YUI Library.
16322  * The original license:
16323  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16324  * Code licensed under the BSD License:
16325  * http://developer.yahoo.net/yui/license.txt
16326  */
16327
16328 (function() {
16329
16330 var Event=Roo.EventManager;
16331 var Dom=Roo.lib.Dom;
16332
16333 /**
16334  * @class Roo.dd.DragDrop
16335  * @extends Roo.util.Observable
16336  * Defines the interface and base operation of items that that can be
16337  * dragged or can be drop targets.  It was designed to be extended, overriding
16338  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16339  * Up to three html elements can be associated with a DragDrop instance:
16340  * <ul>
16341  * <li>linked element: the element that is passed into the constructor.
16342  * This is the element which defines the boundaries for interaction with
16343  * other DragDrop objects.</li>
16344  * <li>handle element(s): The drag operation only occurs if the element that
16345  * was clicked matches a handle element.  By default this is the linked
16346  * element, but there are times that you will want only a portion of the
16347  * linked element to initiate the drag operation, and the setHandleElId()
16348  * method provides a way to define this.</li>
16349  * <li>drag element: this represents the element that would be moved along
16350  * with the cursor during a drag operation.  By default, this is the linked
16351  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16352  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16353  * </li>
16354  * </ul>
16355  * This class should not be instantiated until the onload event to ensure that
16356  * the associated elements are available.
16357  * The following would define a DragDrop obj that would interact with any
16358  * other DragDrop obj in the "group1" group:
16359  * <pre>
16360  *  dd = new Roo.dd.DragDrop("div1", "group1");
16361  * </pre>
16362  * Since none of the event handlers have been implemented, nothing would
16363  * actually happen if you were to run the code above.  Normally you would
16364  * override this class or one of the default implementations, but you can
16365  * also override the methods you want on an instance of the class...
16366  * <pre>
16367  *  dd.onDragDrop = function(e, id) {
16368  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16369  *  }
16370  * </pre>
16371  * @constructor
16372  * @param {String} id of the element that is linked to this instance
16373  * @param {String} sGroup the group of related DragDrop objects
16374  * @param {object} config an object containing configurable attributes
16375  *                Valid properties for DragDrop:
16376  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16377  */
16378 Roo.dd.DragDrop = function(id, sGroup, config) {
16379     if (id) {
16380         this.init(id, sGroup, config);
16381     }
16382     
16383 };
16384
16385 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16386
16387     /**
16388      * The id of the element associated with this object.  This is what we
16389      * refer to as the "linked element" because the size and position of
16390      * this element is used to determine when the drag and drop objects have
16391      * interacted.
16392      * @property id
16393      * @type String
16394      */
16395     id: null,
16396
16397     /**
16398      * Configuration attributes passed into the constructor
16399      * @property config
16400      * @type object
16401      */
16402     config: null,
16403
16404     /**
16405      * The id of the element that will be dragged.  By default this is same
16406      * as the linked element , but could be changed to another element. Ex:
16407      * Roo.dd.DDProxy
16408      * @property dragElId
16409      * @type String
16410      * @private
16411      */
16412     dragElId: null,
16413
16414     /**
16415      * the id of the element that initiates the drag operation.  By default
16416      * this is the linked element, but could be changed to be a child of this
16417      * element.  This lets us do things like only starting the drag when the
16418      * header element within the linked html element is clicked.
16419      * @property handleElId
16420      * @type String
16421      * @private
16422      */
16423     handleElId: null,
16424
16425     /**
16426      * An associative array of HTML tags that will be ignored if clicked.
16427      * @property invalidHandleTypes
16428      * @type {string: string}
16429      */
16430     invalidHandleTypes: null,
16431
16432     /**
16433      * An associative array of ids for elements that will be ignored if clicked
16434      * @property invalidHandleIds
16435      * @type {string: string}
16436      */
16437     invalidHandleIds: null,
16438
16439     /**
16440      * An indexted array of css class names for elements that will be ignored
16441      * if clicked.
16442      * @property invalidHandleClasses
16443      * @type string[]
16444      */
16445     invalidHandleClasses: null,
16446
16447     /**
16448      * The linked element's absolute X position at the time the drag was
16449      * started
16450      * @property startPageX
16451      * @type int
16452      * @private
16453      */
16454     startPageX: 0,
16455
16456     /**
16457      * The linked element's absolute X position at the time the drag was
16458      * started
16459      * @property startPageY
16460      * @type int
16461      * @private
16462      */
16463     startPageY: 0,
16464
16465     /**
16466      * The group defines a logical collection of DragDrop objects that are
16467      * related.  Instances only get events when interacting with other
16468      * DragDrop object in the same group.  This lets us define multiple
16469      * groups using a single DragDrop subclass if we want.
16470      * @property groups
16471      * @type {string: string}
16472      */
16473     groups: null,
16474
16475     /**
16476      * Individual drag/drop instances can be locked.  This will prevent
16477      * onmousedown start drag.
16478      * @property locked
16479      * @type boolean
16480      * @private
16481      */
16482     locked: false,
16483
16484     /**
16485      * Lock this instance
16486      * @method lock
16487      */
16488     lock: function() { this.locked = true; },
16489
16490     /**
16491      * Unlock this instace
16492      * @method unlock
16493      */
16494     unlock: function() { this.locked = false; },
16495
16496     /**
16497      * By default, all insances can be a drop target.  This can be disabled by
16498      * setting isTarget to false.
16499      * @method isTarget
16500      * @type boolean
16501      */
16502     isTarget: true,
16503
16504     /**
16505      * The padding configured for this drag and drop object for calculating
16506      * the drop zone intersection with this object.
16507      * @method padding
16508      * @type int[]
16509      */
16510     padding: null,
16511
16512     /**
16513      * Cached reference to the linked element
16514      * @property _domRef
16515      * @private
16516      */
16517     _domRef: null,
16518
16519     /**
16520      * Internal typeof flag
16521      * @property __ygDragDrop
16522      * @private
16523      */
16524     __ygDragDrop: true,
16525
16526     /**
16527      * Set to true when horizontal contraints are applied
16528      * @property constrainX
16529      * @type boolean
16530      * @private
16531      */
16532     constrainX: false,
16533
16534     /**
16535      * Set to true when vertical contraints are applied
16536      * @property constrainY
16537      * @type boolean
16538      * @private
16539      */
16540     constrainY: false,
16541
16542     /**
16543      * The left constraint
16544      * @property minX
16545      * @type int
16546      * @private
16547      */
16548     minX: 0,
16549
16550     /**
16551      * The right constraint
16552      * @property maxX
16553      * @type int
16554      * @private
16555      */
16556     maxX: 0,
16557
16558     /**
16559      * The up constraint
16560      * @property minY
16561      * @type int
16562      * @type int
16563      * @private
16564      */
16565     minY: 0,
16566
16567     /**
16568      * The down constraint
16569      * @property maxY
16570      * @type int
16571      * @private
16572      */
16573     maxY: 0,
16574
16575     /**
16576      * Maintain offsets when we resetconstraints.  Set to true when you want
16577      * the position of the element relative to its parent to stay the same
16578      * when the page changes
16579      *
16580      * @property maintainOffset
16581      * @type boolean
16582      */
16583     maintainOffset: false,
16584
16585     /**
16586      * Array of pixel locations the element will snap to if we specified a
16587      * horizontal graduation/interval.  This array is generated automatically
16588      * when you define a tick interval.
16589      * @property xTicks
16590      * @type int[]
16591      */
16592     xTicks: null,
16593
16594     /**
16595      * Array of pixel locations the element will snap to if we specified a
16596      * vertical graduation/interval.  This array is generated automatically
16597      * when you define a tick interval.
16598      * @property yTicks
16599      * @type int[]
16600      */
16601     yTicks: null,
16602
16603     /**
16604      * By default the drag and drop instance will only respond to the primary
16605      * button click (left button for a right-handed mouse).  Set to true to
16606      * allow drag and drop to start with any mouse click that is propogated
16607      * by the browser
16608      * @property primaryButtonOnly
16609      * @type boolean
16610      */
16611     primaryButtonOnly: true,
16612
16613     /**
16614      * The availabe property is false until the linked dom element is accessible.
16615      * @property available
16616      * @type boolean
16617      */
16618     available: false,
16619
16620     /**
16621      * By default, drags can only be initiated if the mousedown occurs in the
16622      * region the linked element is.  This is done in part to work around a
16623      * bug in some browsers that mis-report the mousedown if the previous
16624      * mouseup happened outside of the window.  This property is set to true
16625      * if outer handles are defined.
16626      *
16627      * @property hasOuterHandles
16628      * @type boolean
16629      * @default false
16630      */
16631     hasOuterHandles: false,
16632
16633     /**
16634      * Code that executes immediately before the startDrag event
16635      * @method b4StartDrag
16636      * @private
16637      */
16638     b4StartDrag: function(x, y) { },
16639
16640     /**
16641      * Abstract method called after a drag/drop object is clicked
16642      * and the drag or mousedown time thresholds have beeen met.
16643      * @method startDrag
16644      * @param {int} X click location
16645      * @param {int} Y click location
16646      */
16647     startDrag: function(x, y) { /* override this */ },
16648
16649     /**
16650      * Code that executes immediately before the onDrag event
16651      * @method b4Drag
16652      * @private
16653      */
16654     b4Drag: function(e) { },
16655
16656     /**
16657      * Abstract method called during the onMouseMove event while dragging an
16658      * object.
16659      * @method onDrag
16660      * @param {Event} e the mousemove event
16661      */
16662     onDrag: function(e) { /* override this */ },
16663
16664     /**
16665      * Abstract method called when this element fist begins hovering over
16666      * another DragDrop obj
16667      * @method onDragEnter
16668      * @param {Event} e the mousemove event
16669      * @param {String|DragDrop[]} id In POINT mode, the element
16670      * id this is hovering over.  In INTERSECT mode, an array of one or more
16671      * dragdrop items being hovered over.
16672      */
16673     onDragEnter: function(e, id) { /* override this */ },
16674
16675     /**
16676      * Code that executes immediately before the onDragOver event
16677      * @method b4DragOver
16678      * @private
16679      */
16680     b4DragOver: function(e) { },
16681
16682     /**
16683      * Abstract method called when this element is hovering over another
16684      * DragDrop obj
16685      * @method onDragOver
16686      * @param {Event} e the mousemove event
16687      * @param {String|DragDrop[]} id In POINT mode, the element
16688      * id this is hovering over.  In INTERSECT mode, an array of dd items
16689      * being hovered over.
16690      */
16691     onDragOver: function(e, id) { /* override this */ },
16692
16693     /**
16694      * Code that executes immediately before the onDragOut event
16695      * @method b4DragOut
16696      * @private
16697      */
16698     b4DragOut: function(e) { },
16699
16700     /**
16701      * Abstract method called when we are no longer hovering over an element
16702      * @method onDragOut
16703      * @param {Event} e the mousemove event
16704      * @param {String|DragDrop[]} id In POINT mode, the element
16705      * id this was hovering over.  In INTERSECT mode, an array of dd items
16706      * that the mouse is no longer over.
16707      */
16708     onDragOut: function(e, id) { /* override this */ },
16709
16710     /**
16711      * Code that executes immediately before the onDragDrop event
16712      * @method b4DragDrop
16713      * @private
16714      */
16715     b4DragDrop: function(e) { },
16716
16717     /**
16718      * Abstract method called when this item is dropped on another DragDrop
16719      * obj
16720      * @method onDragDrop
16721      * @param {Event} e the mouseup event
16722      * @param {String|DragDrop[]} id In POINT mode, the element
16723      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16724      * was dropped on.
16725      */
16726     onDragDrop: function(e, id) { /* override this */ },
16727
16728     /**
16729      * Abstract method called when this item is dropped on an area with no
16730      * drop target
16731      * @method onInvalidDrop
16732      * @param {Event} e the mouseup event
16733      */
16734     onInvalidDrop: function(e) { /* override this */ },
16735
16736     /**
16737      * Code that executes immediately before the endDrag event
16738      * @method b4EndDrag
16739      * @private
16740      */
16741     b4EndDrag: function(e) { },
16742
16743     /**
16744      * Fired when we are done dragging the object
16745      * @method endDrag
16746      * @param {Event} e the mouseup event
16747      */
16748     endDrag: function(e) { /* override this */ },
16749
16750     /**
16751      * Code executed immediately before the onMouseDown event
16752      * @method b4MouseDown
16753      * @param {Event} e the mousedown event
16754      * @private
16755      */
16756     b4MouseDown: function(e) {  },
16757
16758     /**
16759      * Event handler that fires when a drag/drop obj gets a mousedown
16760      * @method onMouseDown
16761      * @param {Event} e the mousedown event
16762      */
16763     onMouseDown: function(e) { /* override this */ },
16764
16765     /**
16766      * Event handler that fires when a drag/drop obj gets a mouseup
16767      * @method onMouseUp
16768      * @param {Event} e the mouseup event
16769      */
16770     onMouseUp: function(e) { /* override this */ },
16771
16772     /**
16773      * Override the onAvailable method to do what is needed after the initial
16774      * position was determined.
16775      * @method onAvailable
16776      */
16777     onAvailable: function () {
16778     },
16779
16780     /*
16781      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16782      * @type Object
16783      */
16784     defaultPadding : {left:0, right:0, top:0, bottom:0},
16785
16786     /*
16787      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16788  *
16789  * Usage:
16790  <pre><code>
16791  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16792                 { dragElId: "existingProxyDiv" });
16793  dd.startDrag = function(){
16794      this.constrainTo("parent-id");
16795  };
16796  </code></pre>
16797  * Or you can initalize it using the {@link Roo.Element} object:
16798  <pre><code>
16799  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16800      startDrag : function(){
16801          this.constrainTo("parent-id");
16802      }
16803  });
16804  </code></pre>
16805      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16806      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16807      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16808      * an object containing the sides to pad. For example: {right:10, bottom:10}
16809      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16810      */
16811     constrainTo : function(constrainTo, pad, inContent){
16812         if(typeof pad == "number"){
16813             pad = {left: pad, right:pad, top:pad, bottom:pad};
16814         }
16815         pad = pad || this.defaultPadding;
16816         var b = Roo.get(this.getEl()).getBox();
16817         var ce = Roo.get(constrainTo);
16818         var s = ce.getScroll();
16819         var c, cd = ce.dom;
16820         if(cd == document.body){
16821             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16822         }else{
16823             xy = ce.getXY();
16824             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16825         }
16826
16827
16828         var topSpace = b.y - c.y;
16829         var leftSpace = b.x - c.x;
16830
16831         this.resetConstraints();
16832         this.setXConstraint(leftSpace - (pad.left||0), // left
16833                 c.width - leftSpace - b.width - (pad.right||0) //right
16834         );
16835         this.setYConstraint(topSpace - (pad.top||0), //top
16836                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16837         );
16838     },
16839
16840     /**
16841      * Returns a reference to the linked element
16842      * @method getEl
16843      * @return {HTMLElement} the html element
16844      */
16845     getEl: function() {
16846         if (!this._domRef) {
16847             this._domRef = Roo.getDom(this.id);
16848         }
16849
16850         return this._domRef;
16851     },
16852
16853     /**
16854      * Returns a reference to the actual element to drag.  By default this is
16855      * the same as the html element, but it can be assigned to another
16856      * element. An example of this can be found in Roo.dd.DDProxy
16857      * @method getDragEl
16858      * @return {HTMLElement} the html element
16859      */
16860     getDragEl: function() {
16861         return Roo.getDom(this.dragElId);
16862     },
16863
16864     /**
16865      * Sets up the DragDrop object.  Must be called in the constructor of any
16866      * Roo.dd.DragDrop subclass
16867      * @method init
16868      * @param id the id of the linked element
16869      * @param {String} sGroup the group of related items
16870      * @param {object} config configuration attributes
16871      */
16872     init: function(id, sGroup, config) {
16873         this.initTarget(id, sGroup, config);
16874         if (!Roo.isTouch) {
16875             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16876         }
16877         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16878         // Event.on(this.id, "selectstart", Event.preventDefault);
16879     },
16880
16881     /**
16882      * Initializes Targeting functionality only... the object does not
16883      * get a mousedown handler.
16884      * @method initTarget
16885      * @param id the id of the linked element
16886      * @param {String} sGroup the group of related items
16887      * @param {object} config configuration attributes
16888      */
16889     initTarget: function(id, sGroup, config) {
16890
16891         // configuration attributes
16892         this.config = config || {};
16893
16894         // create a local reference to the drag and drop manager
16895         this.DDM = Roo.dd.DDM;
16896         // initialize the groups array
16897         this.groups = {};
16898
16899         // assume that we have an element reference instead of an id if the
16900         // parameter is not a string
16901         if (typeof id !== "string") {
16902             id = Roo.id(id);
16903         }
16904
16905         // set the id
16906         this.id = id;
16907
16908         // add to an interaction group
16909         this.addToGroup((sGroup) ? sGroup : "default");
16910
16911         // We don't want to register this as the handle with the manager
16912         // so we just set the id rather than calling the setter.
16913         this.handleElId = id;
16914
16915         // the linked element is the element that gets dragged by default
16916         this.setDragElId(id);
16917
16918         // by default, clicked anchors will not start drag operations.
16919         this.invalidHandleTypes = { A: "A" };
16920         this.invalidHandleIds = {};
16921         this.invalidHandleClasses = [];
16922
16923         this.applyConfig();
16924
16925         this.handleOnAvailable();
16926     },
16927
16928     /**
16929      * Applies the configuration parameters that were passed into the constructor.
16930      * This is supposed to happen at each level through the inheritance chain.  So
16931      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16932      * DragDrop in order to get all of the parameters that are available in
16933      * each object.
16934      * @method applyConfig
16935      */
16936     applyConfig: function() {
16937
16938         // configurable properties:
16939         //    padding, isTarget, maintainOffset, primaryButtonOnly
16940         this.padding           = this.config.padding || [0, 0, 0, 0];
16941         this.isTarget          = (this.config.isTarget !== false);
16942         this.maintainOffset    = (this.config.maintainOffset);
16943         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16944
16945     },
16946
16947     /**
16948      * Executed when the linked element is available
16949      * @method handleOnAvailable
16950      * @private
16951      */
16952     handleOnAvailable: function() {
16953         this.available = true;
16954         this.resetConstraints();
16955         this.onAvailable();
16956     },
16957
16958      /**
16959      * Configures the padding for the target zone in px.  Effectively expands
16960      * (or reduces) the virtual object size for targeting calculations.
16961      * Supports css-style shorthand; if only one parameter is passed, all sides
16962      * will have that padding, and if only two are passed, the top and bottom
16963      * will have the first param, the left and right the second.
16964      * @method setPadding
16965      * @param {int} iTop    Top pad
16966      * @param {int} iRight  Right pad
16967      * @param {int} iBot    Bot pad
16968      * @param {int} iLeft   Left pad
16969      */
16970     setPadding: function(iTop, iRight, iBot, iLeft) {
16971         // this.padding = [iLeft, iRight, iTop, iBot];
16972         if (!iRight && 0 !== iRight) {
16973             this.padding = [iTop, iTop, iTop, iTop];
16974         } else if (!iBot && 0 !== iBot) {
16975             this.padding = [iTop, iRight, iTop, iRight];
16976         } else {
16977             this.padding = [iTop, iRight, iBot, iLeft];
16978         }
16979     },
16980
16981     /**
16982      * Stores the initial placement of the linked element.
16983      * @method setInitialPosition
16984      * @param {int} diffX   the X offset, default 0
16985      * @param {int} diffY   the Y offset, default 0
16986      */
16987     setInitPosition: function(diffX, diffY) {
16988         var el = this.getEl();
16989
16990         if (!this.DDM.verifyEl(el)) {
16991             return;
16992         }
16993
16994         var dx = diffX || 0;
16995         var dy = diffY || 0;
16996
16997         var p = Dom.getXY( el );
16998
16999         this.initPageX = p[0] - dx;
17000         this.initPageY = p[1] - dy;
17001
17002         this.lastPageX = p[0];
17003         this.lastPageY = p[1];
17004
17005
17006         this.setStartPosition(p);
17007     },
17008
17009     /**
17010      * Sets the start position of the element.  This is set when the obj
17011      * is initialized, the reset when a drag is started.
17012      * @method setStartPosition
17013      * @param pos current position (from previous lookup)
17014      * @private
17015      */
17016     setStartPosition: function(pos) {
17017         var p = pos || Dom.getXY( this.getEl() );
17018         this.deltaSetXY = null;
17019
17020         this.startPageX = p[0];
17021         this.startPageY = p[1];
17022     },
17023
17024     /**
17025      * Add this instance to a group of related drag/drop objects.  All
17026      * instances belong to at least one group, and can belong to as many
17027      * groups as needed.
17028      * @method addToGroup
17029      * @param sGroup {string} the name of the group
17030      */
17031     addToGroup: function(sGroup) {
17032         this.groups[sGroup] = true;
17033         this.DDM.regDragDrop(this, sGroup);
17034     },
17035
17036     /**
17037      * Remove's this instance from the supplied interaction group
17038      * @method removeFromGroup
17039      * @param {string}  sGroup  The group to drop
17040      */
17041     removeFromGroup: function(sGroup) {
17042         if (this.groups[sGroup]) {
17043             delete this.groups[sGroup];
17044         }
17045
17046         this.DDM.removeDDFromGroup(this, sGroup);
17047     },
17048
17049     /**
17050      * Allows you to specify that an element other than the linked element
17051      * will be moved with the cursor during a drag
17052      * @method setDragElId
17053      * @param id {string} the id of the element that will be used to initiate the drag
17054      */
17055     setDragElId: function(id) {
17056         this.dragElId = id;
17057     },
17058
17059     /**
17060      * Allows you to specify a child of the linked element that should be
17061      * used to initiate the drag operation.  An example of this would be if
17062      * you have a content div with text and links.  Clicking anywhere in the
17063      * content area would normally start the drag operation.  Use this method
17064      * to specify that an element inside of the content div is the element
17065      * that starts the drag operation.
17066      * @method setHandleElId
17067      * @param id {string} the id of the element that will be used to
17068      * initiate the drag.
17069      */
17070     setHandleElId: function(id) {
17071         if (typeof id !== "string") {
17072             id = Roo.id(id);
17073         }
17074         this.handleElId = id;
17075         this.DDM.regHandle(this.id, id);
17076     },
17077
17078     /**
17079      * Allows you to set an element outside of the linked element as a drag
17080      * handle
17081      * @method setOuterHandleElId
17082      * @param id the id of the element that will be used to initiate the drag
17083      */
17084     setOuterHandleElId: function(id) {
17085         if (typeof id !== "string") {
17086             id = Roo.id(id);
17087         }
17088         Event.on(id, "mousedown",
17089                 this.handleMouseDown, this);
17090         this.setHandleElId(id);
17091
17092         this.hasOuterHandles = true;
17093     },
17094
17095     /**
17096      * Remove all drag and drop hooks for this element
17097      * @method unreg
17098      */
17099     unreg: function() {
17100         Event.un(this.id, "mousedown",
17101                 this.handleMouseDown);
17102         Event.un(this.id, "touchstart",
17103                 this.handleMouseDown);
17104         this._domRef = null;
17105         this.DDM._remove(this);
17106     },
17107
17108     destroy : function(){
17109         this.unreg();
17110     },
17111
17112     /**
17113      * Returns true if this instance is locked, or the drag drop mgr is locked
17114      * (meaning that all drag/drop is disabled on the page.)
17115      * @method isLocked
17116      * @return {boolean} true if this obj or all drag/drop is locked, else
17117      * false
17118      */
17119     isLocked: function() {
17120         return (this.DDM.isLocked() || this.locked);
17121     },
17122
17123     /**
17124      * Fired when this object is clicked
17125      * @method handleMouseDown
17126      * @param {Event} e
17127      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17128      * @private
17129      */
17130     handleMouseDown: function(e, oDD){
17131      
17132         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17133             //Roo.log('not touch/ button !=0');
17134             return;
17135         }
17136         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17137             return; // double touch..
17138         }
17139         
17140
17141         if (this.isLocked()) {
17142             //Roo.log('locked');
17143             return;
17144         }
17145
17146         this.DDM.refreshCache(this.groups);
17147 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17148         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17149         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17150             //Roo.log('no outer handes or not over target');
17151                 // do nothing.
17152         } else {
17153 //            Roo.log('check validator');
17154             if (this.clickValidator(e)) {
17155 //                Roo.log('validate success');
17156                 // set the initial element position
17157                 this.setStartPosition();
17158
17159
17160                 this.b4MouseDown(e);
17161                 this.onMouseDown(e);
17162
17163                 this.DDM.handleMouseDown(e, this);
17164
17165                 this.DDM.stopEvent(e);
17166             } else {
17167
17168
17169             }
17170         }
17171     },
17172
17173     clickValidator: function(e) {
17174         var target = e.getTarget();
17175         return ( this.isValidHandleChild(target) &&
17176                     (this.id == this.handleElId ||
17177                         this.DDM.handleWasClicked(target, this.id)) );
17178     },
17179
17180     /**
17181      * Allows you to specify a tag name that should not start a drag operation
17182      * when clicked.  This is designed to facilitate embedding links within a
17183      * drag handle that do something other than start the drag.
17184      * @method addInvalidHandleType
17185      * @param {string} tagName the type of element to exclude
17186      */
17187     addInvalidHandleType: function(tagName) {
17188         var type = tagName.toUpperCase();
17189         this.invalidHandleTypes[type] = type;
17190     },
17191
17192     /**
17193      * Lets you to specify an element id for a child of a drag handle
17194      * that should not initiate a drag
17195      * @method addInvalidHandleId
17196      * @param {string} id the element id of the element you wish to ignore
17197      */
17198     addInvalidHandleId: function(id) {
17199         if (typeof id !== "string") {
17200             id = Roo.id(id);
17201         }
17202         this.invalidHandleIds[id] = id;
17203     },
17204
17205     /**
17206      * Lets you specify a css class of elements that will not initiate a drag
17207      * @method addInvalidHandleClass
17208      * @param {string} cssClass the class of the elements you wish to ignore
17209      */
17210     addInvalidHandleClass: function(cssClass) {
17211         this.invalidHandleClasses.push(cssClass);
17212     },
17213
17214     /**
17215      * Unsets an excluded tag name set by addInvalidHandleType
17216      * @method removeInvalidHandleType
17217      * @param {string} tagName the type of element to unexclude
17218      */
17219     removeInvalidHandleType: function(tagName) {
17220         var type = tagName.toUpperCase();
17221         // this.invalidHandleTypes[type] = null;
17222         delete this.invalidHandleTypes[type];
17223     },
17224
17225     /**
17226      * Unsets an invalid handle id
17227      * @method removeInvalidHandleId
17228      * @param {string} id the id of the element to re-enable
17229      */
17230     removeInvalidHandleId: function(id) {
17231         if (typeof id !== "string") {
17232             id = Roo.id(id);
17233         }
17234         delete this.invalidHandleIds[id];
17235     },
17236
17237     /**
17238      * Unsets an invalid css class
17239      * @method removeInvalidHandleClass
17240      * @param {string} cssClass the class of the element(s) you wish to
17241      * re-enable
17242      */
17243     removeInvalidHandleClass: function(cssClass) {
17244         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17245             if (this.invalidHandleClasses[i] == cssClass) {
17246                 delete this.invalidHandleClasses[i];
17247             }
17248         }
17249     },
17250
17251     /**
17252      * Checks the tag exclusion list to see if this click should be ignored
17253      * @method isValidHandleChild
17254      * @param {HTMLElement} node the HTMLElement to evaluate
17255      * @return {boolean} true if this is a valid tag type, false if not
17256      */
17257     isValidHandleChild: function(node) {
17258
17259         var valid = true;
17260         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17261         var nodeName;
17262         try {
17263             nodeName = node.nodeName.toUpperCase();
17264         } catch(e) {
17265             nodeName = node.nodeName;
17266         }
17267         valid = valid && !this.invalidHandleTypes[nodeName];
17268         valid = valid && !this.invalidHandleIds[node.id];
17269
17270         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17271             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17272         }
17273
17274
17275         return valid;
17276
17277     },
17278
17279     /**
17280      * Create the array of horizontal tick marks if an interval was specified
17281      * in setXConstraint().
17282      * @method setXTicks
17283      * @private
17284      */
17285     setXTicks: function(iStartX, iTickSize) {
17286         this.xTicks = [];
17287         this.xTickSize = iTickSize;
17288
17289         var tickMap = {};
17290
17291         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17292             if (!tickMap[i]) {
17293                 this.xTicks[this.xTicks.length] = i;
17294                 tickMap[i] = true;
17295             }
17296         }
17297
17298         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17299             if (!tickMap[i]) {
17300                 this.xTicks[this.xTicks.length] = i;
17301                 tickMap[i] = true;
17302             }
17303         }
17304
17305         this.xTicks.sort(this.DDM.numericSort) ;
17306     },
17307
17308     /**
17309      * Create the array of vertical tick marks if an interval was specified in
17310      * setYConstraint().
17311      * @method setYTicks
17312      * @private
17313      */
17314     setYTicks: function(iStartY, iTickSize) {
17315         this.yTicks = [];
17316         this.yTickSize = iTickSize;
17317
17318         var tickMap = {};
17319
17320         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17321             if (!tickMap[i]) {
17322                 this.yTicks[this.yTicks.length] = i;
17323                 tickMap[i] = true;
17324             }
17325         }
17326
17327         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17328             if (!tickMap[i]) {
17329                 this.yTicks[this.yTicks.length] = i;
17330                 tickMap[i] = true;
17331             }
17332         }
17333
17334         this.yTicks.sort(this.DDM.numericSort) ;
17335     },
17336
17337     /**
17338      * By default, the element can be dragged any place on the screen.  Use
17339      * this method to limit the horizontal travel of the element.  Pass in
17340      * 0,0 for the parameters if you want to lock the drag to the y axis.
17341      * @method setXConstraint
17342      * @param {int} iLeft the number of pixels the element can move to the left
17343      * @param {int} iRight the number of pixels the element can move to the
17344      * right
17345      * @param {int} iTickSize optional parameter for specifying that the
17346      * element
17347      * should move iTickSize pixels at a time.
17348      */
17349     setXConstraint: function(iLeft, iRight, iTickSize) {
17350         this.leftConstraint = iLeft;
17351         this.rightConstraint = iRight;
17352
17353         this.minX = this.initPageX - iLeft;
17354         this.maxX = this.initPageX + iRight;
17355         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17356
17357         this.constrainX = true;
17358     },
17359
17360     /**
17361      * Clears any constraints applied to this instance.  Also clears ticks
17362      * since they can't exist independent of a constraint at this time.
17363      * @method clearConstraints
17364      */
17365     clearConstraints: function() {
17366         this.constrainX = false;
17367         this.constrainY = false;
17368         this.clearTicks();
17369     },
17370
17371     /**
17372      * Clears any tick interval defined for this instance
17373      * @method clearTicks
17374      */
17375     clearTicks: function() {
17376         this.xTicks = null;
17377         this.yTicks = null;
17378         this.xTickSize = 0;
17379         this.yTickSize = 0;
17380     },
17381
17382     /**
17383      * By default, the element can be dragged any place on the screen.  Set
17384      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17385      * parameters if you want to lock the drag to the x axis.
17386      * @method setYConstraint
17387      * @param {int} iUp the number of pixels the element can move up
17388      * @param {int} iDown the number of pixels the element can move down
17389      * @param {int} iTickSize optional parameter for specifying that the
17390      * element should move iTickSize pixels at a time.
17391      */
17392     setYConstraint: function(iUp, iDown, iTickSize) {
17393         this.topConstraint = iUp;
17394         this.bottomConstraint = iDown;
17395
17396         this.minY = this.initPageY - iUp;
17397         this.maxY = this.initPageY + iDown;
17398         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17399
17400         this.constrainY = true;
17401
17402     },
17403
17404     /**
17405      * resetConstraints must be called if you manually reposition a dd element.
17406      * @method resetConstraints
17407      * @param {boolean} maintainOffset
17408      */
17409     resetConstraints: function() {
17410
17411
17412         // Maintain offsets if necessary
17413         if (this.initPageX || this.initPageX === 0) {
17414             // figure out how much this thing has moved
17415             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17416             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17417
17418             this.setInitPosition(dx, dy);
17419
17420         // This is the first time we have detected the element's position
17421         } else {
17422             this.setInitPosition();
17423         }
17424
17425         if (this.constrainX) {
17426             this.setXConstraint( this.leftConstraint,
17427                                  this.rightConstraint,
17428                                  this.xTickSize        );
17429         }
17430
17431         if (this.constrainY) {
17432             this.setYConstraint( this.topConstraint,
17433                                  this.bottomConstraint,
17434                                  this.yTickSize         );
17435         }
17436     },
17437
17438     /**
17439      * Normally the drag element is moved pixel by pixel, but we can specify
17440      * that it move a number of pixels at a time.  This method resolves the
17441      * location when we have it set up like this.
17442      * @method getTick
17443      * @param {int} val where we want to place the object
17444      * @param {int[]} tickArray sorted array of valid points
17445      * @return {int} the closest tick
17446      * @private
17447      */
17448     getTick: function(val, tickArray) {
17449
17450         if (!tickArray) {
17451             // If tick interval is not defined, it is effectively 1 pixel,
17452             // so we return the value passed to us.
17453             return val;
17454         } else if (tickArray[0] >= val) {
17455             // The value is lower than the first tick, so we return the first
17456             // tick.
17457             return tickArray[0];
17458         } else {
17459             for (var i=0, len=tickArray.length; i<len; ++i) {
17460                 var next = i + 1;
17461                 if (tickArray[next] && tickArray[next] >= val) {
17462                     var diff1 = val - tickArray[i];
17463                     var diff2 = tickArray[next] - val;
17464                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17465                 }
17466             }
17467
17468             // The value is larger than the last tick, so we return the last
17469             // tick.
17470             return tickArray[tickArray.length - 1];
17471         }
17472     },
17473
17474     /**
17475      * toString method
17476      * @method toString
17477      * @return {string} string representation of the dd obj
17478      */
17479     toString: function() {
17480         return ("DragDrop " + this.id);
17481     }
17482
17483 });
17484
17485 })();
17486 /*
17487  * Based on:
17488  * Ext JS Library 1.1.1
17489  * Copyright(c) 2006-2007, Ext JS, LLC.
17490  *
17491  * Originally Released Under LGPL - original licence link has changed is not relivant.
17492  *
17493  * Fork - LGPL
17494  * <script type="text/javascript">
17495  */
17496
17497
17498 /**
17499  * The drag and drop utility provides a framework for building drag and drop
17500  * applications.  In addition to enabling drag and drop for specific elements,
17501  * the drag and drop elements are tracked by the manager class, and the
17502  * interactions between the various elements are tracked during the drag and
17503  * the implementing code is notified about these important moments.
17504  */
17505
17506 // Only load the library once.  Rewriting the manager class would orphan
17507 // existing drag and drop instances.
17508 if (!Roo.dd.DragDropMgr) {
17509
17510 /**
17511  * @class Roo.dd.DragDropMgr
17512  * DragDropMgr is a singleton that tracks the element interaction for
17513  * all DragDrop items in the window.  Generally, you will not call
17514  * this class directly, but it does have helper methods that could
17515  * be useful in your DragDrop implementations.
17516  * @singleton
17517  */
17518 Roo.dd.DragDropMgr = function() {
17519
17520     var Event = Roo.EventManager;
17521
17522     return {
17523
17524         /**
17525          * Two dimensional Array of registered DragDrop objects.  The first
17526          * dimension is the DragDrop item group, the second the DragDrop
17527          * object.
17528          * @property ids
17529          * @type {string: string}
17530          * @private
17531          * @static
17532          */
17533         ids: {},
17534
17535         /**
17536          * Array of element ids defined as drag handles.  Used to determine
17537          * if the element that generated the mousedown event is actually the
17538          * handle and not the html element itself.
17539          * @property handleIds
17540          * @type {string: string}
17541          * @private
17542          * @static
17543          */
17544         handleIds: {},
17545
17546         /**
17547          * the DragDrop object that is currently being dragged
17548          * @property dragCurrent
17549          * @type DragDrop
17550          * @private
17551          * @static
17552          **/
17553         dragCurrent: null,
17554
17555         /**
17556          * the DragDrop object(s) that are being hovered over
17557          * @property dragOvers
17558          * @type Array
17559          * @private
17560          * @static
17561          */
17562         dragOvers: {},
17563
17564         /**
17565          * the X distance between the cursor and the object being dragged
17566          * @property deltaX
17567          * @type int
17568          * @private
17569          * @static
17570          */
17571         deltaX: 0,
17572
17573         /**
17574          * the Y distance between the cursor and the object being dragged
17575          * @property deltaY
17576          * @type int
17577          * @private
17578          * @static
17579          */
17580         deltaY: 0,
17581
17582         /**
17583          * Flag to determine if we should prevent the default behavior of the
17584          * events we define. By default this is true, but this can be set to
17585          * false if you need the default behavior (not recommended)
17586          * @property preventDefault
17587          * @type boolean
17588          * @static
17589          */
17590         preventDefault: true,
17591
17592         /**
17593          * Flag to determine if we should stop the propagation of the events
17594          * we generate. This is true by default but you may want to set it to
17595          * false if the html element contains other features that require the
17596          * mouse click.
17597          * @property stopPropagation
17598          * @type boolean
17599          * @static
17600          */
17601         stopPropagation: true,
17602
17603         /**
17604          * Internal flag that is set to true when drag and drop has been
17605          * intialized
17606          * @property initialized
17607          * @private
17608          * @static
17609          */
17610         initalized: false,
17611
17612         /**
17613          * All drag and drop can be disabled.
17614          * @property locked
17615          * @private
17616          * @static
17617          */
17618         locked: false,
17619
17620         /**
17621          * Called the first time an element is registered.
17622          * @method init
17623          * @private
17624          * @static
17625          */
17626         init: function() {
17627             this.initialized = true;
17628         },
17629
17630         /**
17631          * In point mode, drag and drop interaction is defined by the
17632          * location of the cursor during the drag/drop
17633          * @property POINT
17634          * @type int
17635          * @static
17636          */
17637         POINT: 0,
17638
17639         /**
17640          * In intersect mode, drag and drop interactio nis defined by the
17641          * overlap of two or more drag and drop objects.
17642          * @property INTERSECT
17643          * @type int
17644          * @static
17645          */
17646         INTERSECT: 1,
17647
17648         /**
17649          * The current drag and drop mode.  Default: POINT
17650          * @property mode
17651          * @type int
17652          * @static
17653          */
17654         mode: 0,
17655
17656         /**
17657          * Runs method on all drag and drop objects
17658          * @method _execOnAll
17659          * @private
17660          * @static
17661          */
17662         _execOnAll: function(sMethod, args) {
17663             for (var i in this.ids) {
17664                 for (var j in this.ids[i]) {
17665                     var oDD = this.ids[i][j];
17666                     if (! this.isTypeOfDD(oDD)) {
17667                         continue;
17668                     }
17669                     oDD[sMethod].apply(oDD, args);
17670                 }
17671             }
17672         },
17673
17674         /**
17675          * Drag and drop initialization.  Sets up the global event handlers
17676          * @method _onLoad
17677          * @private
17678          * @static
17679          */
17680         _onLoad: function() {
17681
17682             this.init();
17683
17684             if (!Roo.isTouch) {
17685                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17686                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17687             }
17688             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17689             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17690             
17691             Event.on(window,   "unload",    this._onUnload, this, true);
17692             Event.on(window,   "resize",    this._onResize, this, true);
17693             // Event.on(window,   "mouseout",    this._test);
17694
17695         },
17696
17697         /**
17698          * Reset constraints on all drag and drop objs
17699          * @method _onResize
17700          * @private
17701          * @static
17702          */
17703         _onResize: function(e) {
17704             this._execOnAll("resetConstraints", []);
17705         },
17706
17707         /**
17708          * Lock all drag and drop functionality
17709          * @method lock
17710          * @static
17711          */
17712         lock: function() { this.locked = true; },
17713
17714         /**
17715          * Unlock all drag and drop functionality
17716          * @method unlock
17717          * @static
17718          */
17719         unlock: function() { this.locked = false; },
17720
17721         /**
17722          * Is drag and drop locked?
17723          * @method isLocked
17724          * @return {boolean} True if drag and drop is locked, false otherwise.
17725          * @static
17726          */
17727         isLocked: function() { return this.locked; },
17728
17729         /**
17730          * Location cache that is set for all drag drop objects when a drag is
17731          * initiated, cleared when the drag is finished.
17732          * @property locationCache
17733          * @private
17734          * @static
17735          */
17736         locationCache: {},
17737
17738         /**
17739          * Set useCache to false if you want to force object the lookup of each
17740          * drag and drop linked element constantly during a drag.
17741          * @property useCache
17742          * @type boolean
17743          * @static
17744          */
17745         useCache: true,
17746
17747         /**
17748          * The number of pixels that the mouse needs to move after the
17749          * mousedown before the drag is initiated.  Default=3;
17750          * @property clickPixelThresh
17751          * @type int
17752          * @static
17753          */
17754         clickPixelThresh: 3,
17755
17756         /**
17757          * The number of milliseconds after the mousedown event to initiate the
17758          * drag if we don't get a mouseup event. Default=1000
17759          * @property clickTimeThresh
17760          * @type int
17761          * @static
17762          */
17763         clickTimeThresh: 350,
17764
17765         /**
17766          * Flag that indicates that either the drag pixel threshold or the
17767          * mousdown time threshold has been met
17768          * @property dragThreshMet
17769          * @type boolean
17770          * @private
17771          * @static
17772          */
17773         dragThreshMet: false,
17774
17775         /**
17776          * Timeout used for the click time threshold
17777          * @property clickTimeout
17778          * @type Object
17779          * @private
17780          * @static
17781          */
17782         clickTimeout: null,
17783
17784         /**
17785          * The X position of the mousedown event stored for later use when a
17786          * drag threshold is met.
17787          * @property startX
17788          * @type int
17789          * @private
17790          * @static
17791          */
17792         startX: 0,
17793
17794         /**
17795          * The Y position of the mousedown event stored for later use when a
17796          * drag threshold is met.
17797          * @property startY
17798          * @type int
17799          * @private
17800          * @static
17801          */
17802         startY: 0,
17803
17804         /**
17805          * Each DragDrop instance must be registered with the DragDropMgr.
17806          * This is executed in DragDrop.init()
17807          * @method regDragDrop
17808          * @param {DragDrop} oDD the DragDrop object to register
17809          * @param {String} sGroup the name of the group this element belongs to
17810          * @static
17811          */
17812         regDragDrop: function(oDD, sGroup) {
17813             if (!this.initialized) { this.init(); }
17814
17815             if (!this.ids[sGroup]) {
17816                 this.ids[sGroup] = {};
17817             }
17818             this.ids[sGroup][oDD.id] = oDD;
17819         },
17820
17821         /**
17822          * Removes the supplied dd instance from the supplied group. Executed
17823          * by DragDrop.removeFromGroup, so don't call this function directly.
17824          * @method removeDDFromGroup
17825          * @private
17826          * @static
17827          */
17828         removeDDFromGroup: function(oDD, sGroup) {
17829             if (!this.ids[sGroup]) {
17830                 this.ids[sGroup] = {};
17831             }
17832
17833             var obj = this.ids[sGroup];
17834             if (obj && obj[oDD.id]) {
17835                 delete obj[oDD.id];
17836             }
17837         },
17838
17839         /**
17840          * Unregisters a drag and drop item.  This is executed in
17841          * DragDrop.unreg, use that method instead of calling this directly.
17842          * @method _remove
17843          * @private
17844          * @static
17845          */
17846         _remove: function(oDD) {
17847             for (var g in oDD.groups) {
17848                 if (g && this.ids[g][oDD.id]) {
17849                     delete this.ids[g][oDD.id];
17850                 }
17851             }
17852             delete this.handleIds[oDD.id];
17853         },
17854
17855         /**
17856          * Each DragDrop handle element must be registered.  This is done
17857          * automatically when executing DragDrop.setHandleElId()
17858          * @method regHandle
17859          * @param {String} sDDId the DragDrop id this element is a handle for
17860          * @param {String} sHandleId the id of the element that is the drag
17861          * handle
17862          * @static
17863          */
17864         regHandle: function(sDDId, sHandleId) {
17865             if (!this.handleIds[sDDId]) {
17866                 this.handleIds[sDDId] = {};
17867             }
17868             this.handleIds[sDDId][sHandleId] = sHandleId;
17869         },
17870
17871         /**
17872          * Utility function to determine if a given element has been
17873          * registered as a drag drop item.
17874          * @method isDragDrop
17875          * @param {String} id the element id to check
17876          * @return {boolean} true if this element is a DragDrop item,
17877          * false otherwise
17878          * @static
17879          */
17880         isDragDrop: function(id) {
17881             return ( this.getDDById(id) ) ? true : false;
17882         },
17883
17884         /**
17885          * Returns the drag and drop instances that are in all groups the
17886          * passed in instance belongs to.
17887          * @method getRelated
17888          * @param {DragDrop} p_oDD the obj to get related data for
17889          * @param {boolean} bTargetsOnly if true, only return targetable objs
17890          * @return {DragDrop[]} the related instances
17891          * @static
17892          */
17893         getRelated: function(p_oDD, bTargetsOnly) {
17894             var oDDs = [];
17895             for (var i in p_oDD.groups) {
17896                 for (j in this.ids[i]) {
17897                     var dd = this.ids[i][j];
17898                     if (! this.isTypeOfDD(dd)) {
17899                         continue;
17900                     }
17901                     if (!bTargetsOnly || dd.isTarget) {
17902                         oDDs[oDDs.length] = dd;
17903                     }
17904                 }
17905             }
17906
17907             return oDDs;
17908         },
17909
17910         /**
17911          * Returns true if the specified dd target is a legal target for
17912          * the specifice drag obj
17913          * @method isLegalTarget
17914          * @param {DragDrop} the drag obj
17915          * @param {DragDrop} the target
17916          * @return {boolean} true if the target is a legal target for the
17917          * dd obj
17918          * @static
17919          */
17920         isLegalTarget: function (oDD, oTargetDD) {
17921             var targets = this.getRelated(oDD, true);
17922             for (var i=0, len=targets.length;i<len;++i) {
17923                 if (targets[i].id == oTargetDD.id) {
17924                     return true;
17925                 }
17926             }
17927
17928             return false;
17929         },
17930
17931         /**
17932          * My goal is to be able to transparently determine if an object is
17933          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17934          * returns "object", oDD.constructor.toString() always returns
17935          * "DragDrop" and not the name of the subclass.  So for now it just
17936          * evaluates a well-known variable in DragDrop.
17937          * @method isTypeOfDD
17938          * @param {Object} the object to evaluate
17939          * @return {boolean} true if typeof oDD = DragDrop
17940          * @static
17941          */
17942         isTypeOfDD: function (oDD) {
17943             return (oDD && oDD.__ygDragDrop);
17944         },
17945
17946         /**
17947          * Utility function to determine if a given element has been
17948          * registered as a drag drop handle for the given Drag Drop object.
17949          * @method isHandle
17950          * @param {String} id the element id to check
17951          * @return {boolean} true if this element is a DragDrop handle, false
17952          * otherwise
17953          * @static
17954          */
17955         isHandle: function(sDDId, sHandleId) {
17956             return ( this.handleIds[sDDId] &&
17957                             this.handleIds[sDDId][sHandleId] );
17958         },
17959
17960         /**
17961          * Returns the DragDrop instance for a given id
17962          * @method getDDById
17963          * @param {String} id the id of the DragDrop object
17964          * @return {DragDrop} the drag drop object, null if it is not found
17965          * @static
17966          */
17967         getDDById: function(id) {
17968             for (var i in this.ids) {
17969                 if (this.ids[i][id]) {
17970                     return this.ids[i][id];
17971                 }
17972             }
17973             return null;
17974         },
17975
17976         /**
17977          * Fired after a registered DragDrop object gets the mousedown event.
17978          * Sets up the events required to track the object being dragged
17979          * @method handleMouseDown
17980          * @param {Event} e the event
17981          * @param oDD the DragDrop object being dragged
17982          * @private
17983          * @static
17984          */
17985         handleMouseDown: function(e, oDD) {
17986             if(Roo.QuickTips){
17987                 Roo.QuickTips.disable();
17988             }
17989             this.currentTarget = e.getTarget();
17990
17991             this.dragCurrent = oDD;
17992
17993             var el = oDD.getEl();
17994
17995             // track start position
17996             this.startX = e.getPageX();
17997             this.startY = e.getPageY();
17998
17999             this.deltaX = this.startX - el.offsetLeft;
18000             this.deltaY = this.startY - el.offsetTop;
18001
18002             this.dragThreshMet = false;
18003
18004             this.clickTimeout = setTimeout(
18005                     function() {
18006                         var DDM = Roo.dd.DDM;
18007                         DDM.startDrag(DDM.startX, DDM.startY);
18008                     },
18009                     this.clickTimeThresh );
18010         },
18011
18012         /**
18013          * Fired when either the drag pixel threshol or the mousedown hold
18014          * time threshold has been met.
18015          * @method startDrag
18016          * @param x {int} the X position of the original mousedown
18017          * @param y {int} the Y position of the original mousedown
18018          * @static
18019          */
18020         startDrag: function(x, y) {
18021             clearTimeout(this.clickTimeout);
18022             if (this.dragCurrent) {
18023                 this.dragCurrent.b4StartDrag(x, y);
18024                 this.dragCurrent.startDrag(x, y);
18025             }
18026             this.dragThreshMet = true;
18027         },
18028
18029         /**
18030          * Internal function to handle the mouseup event.  Will be invoked
18031          * from the context of the document.
18032          * @method handleMouseUp
18033          * @param {Event} e the event
18034          * @private
18035          * @static
18036          */
18037         handleMouseUp: function(e) {
18038
18039             if(Roo.QuickTips){
18040                 Roo.QuickTips.enable();
18041             }
18042             if (! this.dragCurrent) {
18043                 return;
18044             }
18045
18046             clearTimeout(this.clickTimeout);
18047
18048             if (this.dragThreshMet) {
18049                 this.fireEvents(e, true);
18050             } else {
18051             }
18052
18053             this.stopDrag(e);
18054
18055             this.stopEvent(e);
18056         },
18057
18058         /**
18059          * Utility to stop event propagation and event default, if these
18060          * features are turned on.
18061          * @method stopEvent
18062          * @param {Event} e the event as returned by this.getEvent()
18063          * @static
18064          */
18065         stopEvent: function(e){
18066             if(this.stopPropagation) {
18067                 e.stopPropagation();
18068             }
18069
18070             if (this.preventDefault) {
18071                 e.preventDefault();
18072             }
18073         },
18074
18075         /**
18076          * Internal function to clean up event handlers after the drag
18077          * operation is complete
18078          * @method stopDrag
18079          * @param {Event} e the event
18080          * @private
18081          * @static
18082          */
18083         stopDrag: function(e) {
18084             // Fire the drag end event for the item that was dragged
18085             if (this.dragCurrent) {
18086                 if (this.dragThreshMet) {
18087                     this.dragCurrent.b4EndDrag(e);
18088                     this.dragCurrent.endDrag(e);
18089                 }
18090
18091                 this.dragCurrent.onMouseUp(e);
18092             }
18093
18094             this.dragCurrent = null;
18095             this.dragOvers = {};
18096         },
18097
18098         /**
18099          * Internal function to handle the mousemove event.  Will be invoked
18100          * from the context of the html element.
18101          *
18102          * @TODO figure out what we can do about mouse events lost when the
18103          * user drags objects beyond the window boundary.  Currently we can
18104          * detect this in internet explorer by verifying that the mouse is
18105          * down during the mousemove event.  Firefox doesn't give us the
18106          * button state on the mousemove event.
18107          * @method handleMouseMove
18108          * @param {Event} e the event
18109          * @private
18110          * @static
18111          */
18112         handleMouseMove: function(e) {
18113             if (! this.dragCurrent) {
18114                 return true;
18115             }
18116
18117             // var button = e.which || e.button;
18118
18119             // check for IE mouseup outside of page boundary
18120             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18121                 this.stopEvent(e);
18122                 return this.handleMouseUp(e);
18123             }
18124
18125             if (!this.dragThreshMet) {
18126                 var diffX = Math.abs(this.startX - e.getPageX());
18127                 var diffY = Math.abs(this.startY - e.getPageY());
18128                 if (diffX > this.clickPixelThresh ||
18129                             diffY > this.clickPixelThresh) {
18130                     this.startDrag(this.startX, this.startY);
18131                 }
18132             }
18133
18134             if (this.dragThreshMet) {
18135                 this.dragCurrent.b4Drag(e);
18136                 this.dragCurrent.onDrag(e);
18137                 if(!this.dragCurrent.moveOnly){
18138                     this.fireEvents(e, false);
18139                 }
18140             }
18141
18142             this.stopEvent(e);
18143
18144             return true;
18145         },
18146
18147         /**
18148          * Iterates over all of the DragDrop elements to find ones we are
18149          * hovering over or dropping on
18150          * @method fireEvents
18151          * @param {Event} e the event
18152          * @param {boolean} isDrop is this a drop op or a mouseover op?
18153          * @private
18154          * @static
18155          */
18156         fireEvents: function(e, isDrop) {
18157             var dc = this.dragCurrent;
18158
18159             // If the user did the mouse up outside of the window, we could
18160             // get here even though we have ended the drag.
18161             if (!dc || dc.isLocked()) {
18162                 return;
18163             }
18164
18165             var pt = e.getPoint();
18166
18167             // cache the previous dragOver array
18168             var oldOvers = [];
18169
18170             var outEvts   = [];
18171             var overEvts  = [];
18172             var dropEvts  = [];
18173             var enterEvts = [];
18174
18175             // Check to see if the object(s) we were hovering over is no longer
18176             // being hovered over so we can fire the onDragOut event
18177             for (var i in this.dragOvers) {
18178
18179                 var ddo = this.dragOvers[i];
18180
18181                 if (! this.isTypeOfDD(ddo)) {
18182                     continue;
18183                 }
18184
18185                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18186                     outEvts.push( ddo );
18187                 }
18188
18189                 oldOvers[i] = true;
18190                 delete this.dragOvers[i];
18191             }
18192
18193             for (var sGroup in dc.groups) {
18194
18195                 if ("string" != typeof sGroup) {
18196                     continue;
18197                 }
18198
18199                 for (i in this.ids[sGroup]) {
18200                     var oDD = this.ids[sGroup][i];
18201                     if (! this.isTypeOfDD(oDD)) {
18202                         continue;
18203                     }
18204
18205                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18206                         if (this.isOverTarget(pt, oDD, this.mode)) {
18207                             // look for drop interactions
18208                             if (isDrop) {
18209                                 dropEvts.push( oDD );
18210                             // look for drag enter and drag over interactions
18211                             } else {
18212
18213                                 // initial drag over: dragEnter fires
18214                                 if (!oldOvers[oDD.id]) {
18215                                     enterEvts.push( oDD );
18216                                 // subsequent drag overs: dragOver fires
18217                                 } else {
18218                                     overEvts.push( oDD );
18219                                 }
18220
18221                                 this.dragOvers[oDD.id] = oDD;
18222                             }
18223                         }
18224                     }
18225                 }
18226             }
18227
18228             if (this.mode) {
18229                 if (outEvts.length) {
18230                     dc.b4DragOut(e, outEvts);
18231                     dc.onDragOut(e, outEvts);
18232                 }
18233
18234                 if (enterEvts.length) {
18235                     dc.onDragEnter(e, enterEvts);
18236                 }
18237
18238                 if (overEvts.length) {
18239                     dc.b4DragOver(e, overEvts);
18240                     dc.onDragOver(e, overEvts);
18241                 }
18242
18243                 if (dropEvts.length) {
18244                     dc.b4DragDrop(e, dropEvts);
18245                     dc.onDragDrop(e, dropEvts);
18246                 }
18247
18248             } else {
18249                 // fire dragout events
18250                 var len = 0;
18251                 for (i=0, len=outEvts.length; i<len; ++i) {
18252                     dc.b4DragOut(e, outEvts[i].id);
18253                     dc.onDragOut(e, outEvts[i].id);
18254                 }
18255
18256                 // fire enter events
18257                 for (i=0,len=enterEvts.length; i<len; ++i) {
18258                     // dc.b4DragEnter(e, oDD.id);
18259                     dc.onDragEnter(e, enterEvts[i].id);
18260                 }
18261
18262                 // fire over events
18263                 for (i=0,len=overEvts.length; i<len; ++i) {
18264                     dc.b4DragOver(e, overEvts[i].id);
18265                     dc.onDragOver(e, overEvts[i].id);
18266                 }
18267
18268                 // fire drop events
18269                 for (i=0, len=dropEvts.length; i<len; ++i) {
18270                     dc.b4DragDrop(e, dropEvts[i].id);
18271                     dc.onDragDrop(e, dropEvts[i].id);
18272                 }
18273
18274             }
18275
18276             // notify about a drop that did not find a target
18277             if (isDrop && !dropEvts.length) {
18278                 dc.onInvalidDrop(e);
18279             }
18280
18281         },
18282
18283         /**
18284          * Helper function for getting the best match from the list of drag
18285          * and drop objects returned by the drag and drop events when we are
18286          * in INTERSECT mode.  It returns either the first object that the
18287          * cursor is over, or the object that has the greatest overlap with
18288          * the dragged element.
18289          * @method getBestMatch
18290          * @param  {DragDrop[]} dds The array of drag and drop objects
18291          * targeted
18292          * @return {DragDrop}       The best single match
18293          * @static
18294          */
18295         getBestMatch: function(dds) {
18296             var winner = null;
18297             // Return null if the input is not what we expect
18298             //if (!dds || !dds.length || dds.length == 0) {
18299                // winner = null;
18300             // If there is only one item, it wins
18301             //} else if (dds.length == 1) {
18302
18303             var len = dds.length;
18304
18305             if (len == 1) {
18306                 winner = dds[0];
18307             } else {
18308                 // Loop through the targeted items
18309                 for (var i=0; i<len; ++i) {
18310                     var dd = dds[i];
18311                     // If the cursor is over the object, it wins.  If the
18312                     // cursor is over multiple matches, the first one we come
18313                     // to wins.
18314                     if (dd.cursorIsOver) {
18315                         winner = dd;
18316                         break;
18317                     // Otherwise the object with the most overlap wins
18318                     } else {
18319                         if (!winner ||
18320                             winner.overlap.getArea() < dd.overlap.getArea()) {
18321                             winner = dd;
18322                         }
18323                     }
18324                 }
18325             }
18326
18327             return winner;
18328         },
18329
18330         /**
18331          * Refreshes the cache of the top-left and bottom-right points of the
18332          * drag and drop objects in the specified group(s).  This is in the
18333          * format that is stored in the drag and drop instance, so typical
18334          * usage is:
18335          * <code>
18336          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18337          * </code>
18338          * Alternatively:
18339          * <code>
18340          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18341          * </code>
18342          * @TODO this really should be an indexed array.  Alternatively this
18343          * method could accept both.
18344          * @method refreshCache
18345          * @param {Object} groups an associative array of groups to refresh
18346          * @static
18347          */
18348         refreshCache: function(groups) {
18349             for (var sGroup in groups) {
18350                 if ("string" != typeof sGroup) {
18351                     continue;
18352                 }
18353                 for (var i in this.ids[sGroup]) {
18354                     var oDD = this.ids[sGroup][i];
18355
18356                     if (this.isTypeOfDD(oDD)) {
18357                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18358                         var loc = this.getLocation(oDD);
18359                         if (loc) {
18360                             this.locationCache[oDD.id] = loc;
18361                         } else {
18362                             delete this.locationCache[oDD.id];
18363                             // this will unregister the drag and drop object if
18364                             // the element is not in a usable state
18365                             // oDD.unreg();
18366                         }
18367                     }
18368                 }
18369             }
18370         },
18371
18372         /**
18373          * This checks to make sure an element exists and is in the DOM.  The
18374          * main purpose is to handle cases where innerHTML is used to remove
18375          * drag and drop objects from the DOM.  IE provides an 'unspecified
18376          * error' when trying to access the offsetParent of such an element
18377          * @method verifyEl
18378          * @param {HTMLElement} el the element to check
18379          * @return {boolean} true if the element looks usable
18380          * @static
18381          */
18382         verifyEl: function(el) {
18383             if (el) {
18384                 var parent;
18385                 if(Roo.isIE){
18386                     try{
18387                         parent = el.offsetParent;
18388                     }catch(e){}
18389                 }else{
18390                     parent = el.offsetParent;
18391                 }
18392                 if (parent) {
18393                     return true;
18394                 }
18395             }
18396
18397             return false;
18398         },
18399
18400         /**
18401          * Returns a Region object containing the drag and drop element's position
18402          * and size, including the padding configured for it
18403          * @method getLocation
18404          * @param {DragDrop} oDD the drag and drop object to get the
18405          *                       location for
18406          * @return {Roo.lib.Region} a Region object representing the total area
18407          *                             the element occupies, including any padding
18408          *                             the instance is configured for.
18409          * @static
18410          */
18411         getLocation: function(oDD) {
18412             if (! this.isTypeOfDD(oDD)) {
18413                 return null;
18414             }
18415
18416             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18417
18418             try {
18419                 pos= Roo.lib.Dom.getXY(el);
18420             } catch (e) { }
18421
18422             if (!pos) {
18423                 return null;
18424             }
18425
18426             x1 = pos[0];
18427             x2 = x1 + el.offsetWidth;
18428             y1 = pos[1];
18429             y2 = y1 + el.offsetHeight;
18430
18431             t = y1 - oDD.padding[0];
18432             r = x2 + oDD.padding[1];
18433             b = y2 + oDD.padding[2];
18434             l = x1 - oDD.padding[3];
18435
18436             return new Roo.lib.Region( t, r, b, l );
18437         },
18438
18439         /**
18440          * Checks the cursor location to see if it over the target
18441          * @method isOverTarget
18442          * @param {Roo.lib.Point} pt The point to evaluate
18443          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18444          * @return {boolean} true if the mouse is over the target
18445          * @private
18446          * @static
18447          */
18448         isOverTarget: function(pt, oTarget, intersect) {
18449             // use cache if available
18450             var loc = this.locationCache[oTarget.id];
18451             if (!loc || !this.useCache) {
18452                 loc = this.getLocation(oTarget);
18453                 this.locationCache[oTarget.id] = loc;
18454
18455             }
18456
18457             if (!loc) {
18458                 return false;
18459             }
18460
18461             oTarget.cursorIsOver = loc.contains( pt );
18462
18463             // DragDrop is using this as a sanity check for the initial mousedown
18464             // in this case we are done.  In POINT mode, if the drag obj has no
18465             // contraints, we are also done. Otherwise we need to evaluate the
18466             // location of the target as related to the actual location of the
18467             // dragged element.
18468             var dc = this.dragCurrent;
18469             if (!dc || !dc.getTargetCoord ||
18470                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18471                 return oTarget.cursorIsOver;
18472             }
18473
18474             oTarget.overlap = null;
18475
18476             // Get the current location of the drag element, this is the
18477             // location of the mouse event less the delta that represents
18478             // where the original mousedown happened on the element.  We
18479             // need to consider constraints and ticks as well.
18480             var pos = dc.getTargetCoord(pt.x, pt.y);
18481
18482             var el = dc.getDragEl();
18483             var curRegion = new Roo.lib.Region( pos.y,
18484                                                    pos.x + el.offsetWidth,
18485                                                    pos.y + el.offsetHeight,
18486                                                    pos.x );
18487
18488             var overlap = curRegion.intersect(loc);
18489
18490             if (overlap) {
18491                 oTarget.overlap = overlap;
18492                 return (intersect) ? true : oTarget.cursorIsOver;
18493             } else {
18494                 return false;
18495             }
18496         },
18497
18498         /**
18499          * unload event handler
18500          * @method _onUnload
18501          * @private
18502          * @static
18503          */
18504         _onUnload: function(e, me) {
18505             Roo.dd.DragDropMgr.unregAll();
18506         },
18507
18508         /**
18509          * Cleans up the drag and drop events and objects.
18510          * @method unregAll
18511          * @private
18512          * @static
18513          */
18514         unregAll: function() {
18515
18516             if (this.dragCurrent) {
18517                 this.stopDrag();
18518                 this.dragCurrent = null;
18519             }
18520
18521             this._execOnAll("unreg", []);
18522
18523             for (i in this.elementCache) {
18524                 delete this.elementCache[i];
18525             }
18526
18527             this.elementCache = {};
18528             this.ids = {};
18529         },
18530
18531         /**
18532          * A cache of DOM elements
18533          * @property elementCache
18534          * @private
18535          * @static
18536          */
18537         elementCache: {},
18538
18539         /**
18540          * Get the wrapper for the DOM element specified
18541          * @method getElWrapper
18542          * @param {String} id the id of the element to get
18543          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18544          * @private
18545          * @deprecated This wrapper isn't that useful
18546          * @static
18547          */
18548         getElWrapper: function(id) {
18549             var oWrapper = this.elementCache[id];
18550             if (!oWrapper || !oWrapper.el) {
18551                 oWrapper = this.elementCache[id] =
18552                     new this.ElementWrapper(Roo.getDom(id));
18553             }
18554             return oWrapper;
18555         },
18556
18557         /**
18558          * Returns the actual DOM element
18559          * @method getElement
18560          * @param {String} id the id of the elment to get
18561          * @return {Object} The element
18562          * @deprecated use Roo.getDom instead
18563          * @static
18564          */
18565         getElement: function(id) {
18566             return Roo.getDom(id);
18567         },
18568
18569         /**
18570          * Returns the style property for the DOM element (i.e.,
18571          * document.getElById(id).style)
18572          * @method getCss
18573          * @param {String} id the id of the elment to get
18574          * @return {Object} The style property of the element
18575          * @deprecated use Roo.getDom instead
18576          * @static
18577          */
18578         getCss: function(id) {
18579             var el = Roo.getDom(id);
18580             return (el) ? el.style : null;
18581         },
18582
18583         /**
18584          * Inner class for cached elements
18585          * @class DragDropMgr.ElementWrapper
18586          * @for DragDropMgr
18587          * @private
18588          * @deprecated
18589          */
18590         ElementWrapper: function(el) {
18591                 /**
18592                  * The element
18593                  * @property el
18594                  */
18595                 this.el = el || null;
18596                 /**
18597                  * The element id
18598                  * @property id
18599                  */
18600                 this.id = this.el && el.id;
18601                 /**
18602                  * A reference to the style property
18603                  * @property css
18604                  */
18605                 this.css = this.el && el.style;
18606             },
18607
18608         /**
18609          * Returns the X position of an html element
18610          * @method getPosX
18611          * @param el the element for which to get the position
18612          * @return {int} the X coordinate
18613          * @for DragDropMgr
18614          * @deprecated use Roo.lib.Dom.getX instead
18615          * @static
18616          */
18617         getPosX: function(el) {
18618             return Roo.lib.Dom.getX(el);
18619         },
18620
18621         /**
18622          * Returns the Y position of an html element
18623          * @method getPosY
18624          * @param el the element for which to get the position
18625          * @return {int} the Y coordinate
18626          * @deprecated use Roo.lib.Dom.getY instead
18627          * @static
18628          */
18629         getPosY: function(el) {
18630             return Roo.lib.Dom.getY(el);
18631         },
18632
18633         /**
18634          * Swap two nodes.  In IE, we use the native method, for others we
18635          * emulate the IE behavior
18636          * @method swapNode
18637          * @param n1 the first node to swap
18638          * @param n2 the other node to swap
18639          * @static
18640          */
18641         swapNode: function(n1, n2) {
18642             if (n1.swapNode) {
18643                 n1.swapNode(n2);
18644             } else {
18645                 var p = n2.parentNode;
18646                 var s = n2.nextSibling;
18647
18648                 if (s == n1) {
18649                     p.insertBefore(n1, n2);
18650                 } else if (n2 == n1.nextSibling) {
18651                     p.insertBefore(n2, n1);
18652                 } else {
18653                     n1.parentNode.replaceChild(n2, n1);
18654                     p.insertBefore(n1, s);
18655                 }
18656             }
18657         },
18658
18659         /**
18660          * Returns the current scroll position
18661          * @method getScroll
18662          * @private
18663          * @static
18664          */
18665         getScroll: function () {
18666             var t, l, dde=document.documentElement, db=document.body;
18667             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18668                 t = dde.scrollTop;
18669                 l = dde.scrollLeft;
18670             } else if (db) {
18671                 t = db.scrollTop;
18672                 l = db.scrollLeft;
18673             } else {
18674
18675             }
18676             return { top: t, left: l };
18677         },
18678
18679         /**
18680          * Returns the specified element style property
18681          * @method getStyle
18682          * @param {HTMLElement} el          the element
18683          * @param {string}      styleProp   the style property
18684          * @return {string} The value of the style property
18685          * @deprecated use Roo.lib.Dom.getStyle
18686          * @static
18687          */
18688         getStyle: function(el, styleProp) {
18689             return Roo.fly(el).getStyle(styleProp);
18690         },
18691
18692         /**
18693          * Gets the scrollTop
18694          * @method getScrollTop
18695          * @return {int} the document's scrollTop
18696          * @static
18697          */
18698         getScrollTop: function () { return this.getScroll().top; },
18699
18700         /**
18701          * Gets the scrollLeft
18702          * @method getScrollLeft
18703          * @return {int} the document's scrollTop
18704          * @static
18705          */
18706         getScrollLeft: function () { return this.getScroll().left; },
18707
18708         /**
18709          * Sets the x/y position of an element to the location of the
18710          * target element.
18711          * @method moveToEl
18712          * @param {HTMLElement} moveEl      The element to move
18713          * @param {HTMLElement} targetEl    The position reference element
18714          * @static
18715          */
18716         moveToEl: function (moveEl, targetEl) {
18717             var aCoord = Roo.lib.Dom.getXY(targetEl);
18718             Roo.lib.Dom.setXY(moveEl, aCoord);
18719         },
18720
18721         /**
18722          * Numeric array sort function
18723          * @method numericSort
18724          * @static
18725          */
18726         numericSort: function(a, b) { return (a - b); },
18727
18728         /**
18729          * Internal counter
18730          * @property _timeoutCount
18731          * @private
18732          * @static
18733          */
18734         _timeoutCount: 0,
18735
18736         /**
18737          * Trying to make the load order less important.  Without this we get
18738          * an error if this file is loaded before the Event Utility.
18739          * @method _addListeners
18740          * @private
18741          * @static
18742          */
18743         _addListeners: function() {
18744             var DDM = Roo.dd.DDM;
18745             if ( Roo.lib.Event && document ) {
18746                 DDM._onLoad();
18747             } else {
18748                 if (DDM._timeoutCount > 2000) {
18749                 } else {
18750                     setTimeout(DDM._addListeners, 10);
18751                     if (document && document.body) {
18752                         DDM._timeoutCount += 1;
18753                     }
18754                 }
18755             }
18756         },
18757
18758         /**
18759          * Recursively searches the immediate parent and all child nodes for
18760          * the handle element in order to determine wheter or not it was
18761          * clicked.
18762          * @method handleWasClicked
18763          * @param node the html element to inspect
18764          * @static
18765          */
18766         handleWasClicked: function(node, id) {
18767             if (this.isHandle(id, node.id)) {
18768                 return true;
18769             } else {
18770                 // check to see if this is a text node child of the one we want
18771                 var p = node.parentNode;
18772
18773                 while (p) {
18774                     if (this.isHandle(id, p.id)) {
18775                         return true;
18776                     } else {
18777                         p = p.parentNode;
18778                     }
18779                 }
18780             }
18781
18782             return false;
18783         }
18784
18785     };
18786
18787 }();
18788
18789 // shorter alias, save a few bytes
18790 Roo.dd.DDM = Roo.dd.DragDropMgr;
18791 Roo.dd.DDM._addListeners();
18792
18793 }/*
18794  * Based on:
18795  * Ext JS Library 1.1.1
18796  * Copyright(c) 2006-2007, Ext JS, LLC.
18797  *
18798  * Originally Released Under LGPL - original licence link has changed is not relivant.
18799  *
18800  * Fork - LGPL
18801  * <script type="text/javascript">
18802  */
18803
18804 /**
18805  * @class Roo.dd.DD
18806  * A DragDrop implementation where the linked element follows the
18807  * mouse cursor during a drag.
18808  * @extends Roo.dd.DragDrop
18809  * @constructor
18810  * @param {String} id the id of the linked element
18811  * @param {String} sGroup the group of related DragDrop items
18812  * @param {object} config an object containing configurable attributes
18813  *                Valid properties for DD:
18814  *                    scroll
18815  */
18816 Roo.dd.DD = function(id, sGroup, config) {
18817     if (id) {
18818         this.init(id, sGroup, config);
18819     }
18820 };
18821
18822 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18823
18824     /**
18825      * When set to true, the utility automatically tries to scroll the browser
18826      * window wehn a drag and drop element is dragged near the viewport boundary.
18827      * Defaults to true.
18828      * @property scroll
18829      * @type boolean
18830      */
18831     scroll: true,
18832
18833     /**
18834      * Sets the pointer offset to the distance between the linked element's top
18835      * left corner and the location the element was clicked
18836      * @method autoOffset
18837      * @param {int} iPageX the X coordinate of the click
18838      * @param {int} iPageY the Y coordinate of the click
18839      */
18840     autoOffset: function(iPageX, iPageY) {
18841         var x = iPageX - this.startPageX;
18842         var y = iPageY - this.startPageY;
18843         this.setDelta(x, y);
18844     },
18845
18846     /**
18847      * Sets the pointer offset.  You can call this directly to force the
18848      * offset to be in a particular location (e.g., pass in 0,0 to set it
18849      * to the center of the object)
18850      * @method setDelta
18851      * @param {int} iDeltaX the distance from the left
18852      * @param {int} iDeltaY the distance from the top
18853      */
18854     setDelta: function(iDeltaX, iDeltaY) {
18855         this.deltaX = iDeltaX;
18856         this.deltaY = iDeltaY;
18857     },
18858
18859     /**
18860      * Sets the drag element to the location of the mousedown or click event,
18861      * maintaining the cursor location relative to the location on the element
18862      * that was clicked.  Override this if you want to place the element in a
18863      * location other than where the cursor is.
18864      * @method setDragElPos
18865      * @param {int} iPageX the X coordinate of the mousedown or drag event
18866      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18867      */
18868     setDragElPos: function(iPageX, iPageY) {
18869         // the first time we do this, we are going to check to make sure
18870         // the element has css positioning
18871
18872         var el = this.getDragEl();
18873         this.alignElWithMouse(el, iPageX, iPageY);
18874     },
18875
18876     /**
18877      * Sets the element to the location of the mousedown or click event,
18878      * maintaining the cursor location relative to the location on the element
18879      * that was clicked.  Override this if you want to place the element in a
18880      * location other than where the cursor is.
18881      * @method alignElWithMouse
18882      * @param {HTMLElement} el the element to move
18883      * @param {int} iPageX the X coordinate of the mousedown or drag event
18884      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18885      */
18886     alignElWithMouse: function(el, iPageX, iPageY) {
18887         var oCoord = this.getTargetCoord(iPageX, iPageY);
18888         var fly = el.dom ? el : Roo.fly(el);
18889         if (!this.deltaSetXY) {
18890             var aCoord = [oCoord.x, oCoord.y];
18891             fly.setXY(aCoord);
18892             var newLeft = fly.getLeft(true);
18893             var newTop  = fly.getTop(true);
18894             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18895         } else {
18896             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18897         }
18898
18899         this.cachePosition(oCoord.x, oCoord.y);
18900         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18901         return oCoord;
18902     },
18903
18904     /**
18905      * Saves the most recent position so that we can reset the constraints and
18906      * tick marks on-demand.  We need to know this so that we can calculate the
18907      * number of pixels the element is offset from its original position.
18908      * @method cachePosition
18909      * @param iPageX the current x position (optional, this just makes it so we
18910      * don't have to look it up again)
18911      * @param iPageY the current y position (optional, this just makes it so we
18912      * don't have to look it up again)
18913      */
18914     cachePosition: function(iPageX, iPageY) {
18915         if (iPageX) {
18916             this.lastPageX = iPageX;
18917             this.lastPageY = iPageY;
18918         } else {
18919             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18920             this.lastPageX = aCoord[0];
18921             this.lastPageY = aCoord[1];
18922         }
18923     },
18924
18925     /**
18926      * Auto-scroll the window if the dragged object has been moved beyond the
18927      * visible window boundary.
18928      * @method autoScroll
18929      * @param {int} x the drag element's x position
18930      * @param {int} y the drag element's y position
18931      * @param {int} h the height of the drag element
18932      * @param {int} w the width of the drag element
18933      * @private
18934      */
18935     autoScroll: function(x, y, h, w) {
18936
18937         if (this.scroll) {
18938             // The client height
18939             var clientH = Roo.lib.Dom.getViewWidth();
18940
18941             // The client width
18942             var clientW = Roo.lib.Dom.getViewHeight();
18943
18944             // The amt scrolled down
18945             var st = this.DDM.getScrollTop();
18946
18947             // The amt scrolled right
18948             var sl = this.DDM.getScrollLeft();
18949
18950             // Location of the bottom of the element
18951             var bot = h + y;
18952
18953             // Location of the right of the element
18954             var right = w + x;
18955
18956             // The distance from the cursor to the bottom of the visible area,
18957             // adjusted so that we don't scroll if the cursor is beyond the
18958             // element drag constraints
18959             var toBot = (clientH + st - y - this.deltaY);
18960
18961             // The distance from the cursor to the right of the visible area
18962             var toRight = (clientW + sl - x - this.deltaX);
18963
18964
18965             // How close to the edge the cursor must be before we scroll
18966             // var thresh = (document.all) ? 100 : 40;
18967             var thresh = 40;
18968
18969             // How many pixels to scroll per autoscroll op.  This helps to reduce
18970             // clunky scrolling. IE is more sensitive about this ... it needs this
18971             // value to be higher.
18972             var scrAmt = (document.all) ? 80 : 30;
18973
18974             // Scroll down if we are near the bottom of the visible page and the
18975             // obj extends below the crease
18976             if ( bot > clientH && toBot < thresh ) {
18977                 window.scrollTo(sl, st + scrAmt);
18978             }
18979
18980             // Scroll up if the window is scrolled down and the top of the object
18981             // goes above the top border
18982             if ( y < st && st > 0 && y - st < thresh ) {
18983                 window.scrollTo(sl, st - scrAmt);
18984             }
18985
18986             // Scroll right if the obj is beyond the right border and the cursor is
18987             // near the border.
18988             if ( right > clientW && toRight < thresh ) {
18989                 window.scrollTo(sl + scrAmt, st);
18990             }
18991
18992             // Scroll left if the window has been scrolled to the right and the obj
18993             // extends past the left border
18994             if ( x < sl && sl > 0 && x - sl < thresh ) {
18995                 window.scrollTo(sl - scrAmt, st);
18996             }
18997         }
18998     },
18999
19000     /**
19001      * Finds the location the element should be placed if we want to move
19002      * it to where the mouse location less the click offset would place us.
19003      * @method getTargetCoord
19004      * @param {int} iPageX the X coordinate of the click
19005      * @param {int} iPageY the Y coordinate of the click
19006      * @return an object that contains the coordinates (Object.x and Object.y)
19007      * @private
19008      */
19009     getTargetCoord: function(iPageX, iPageY) {
19010
19011
19012         var x = iPageX - this.deltaX;
19013         var y = iPageY - this.deltaY;
19014
19015         if (this.constrainX) {
19016             if (x < this.minX) { x = this.minX; }
19017             if (x > this.maxX) { x = this.maxX; }
19018         }
19019
19020         if (this.constrainY) {
19021             if (y < this.minY) { y = this.minY; }
19022             if (y > this.maxY) { y = this.maxY; }
19023         }
19024
19025         x = this.getTick(x, this.xTicks);
19026         y = this.getTick(y, this.yTicks);
19027
19028
19029         return {x:x, y:y};
19030     },
19031
19032     /*
19033      * Sets up config options specific to this class. Overrides
19034      * Roo.dd.DragDrop, but all versions of this method through the
19035      * inheritance chain are called
19036      */
19037     applyConfig: function() {
19038         Roo.dd.DD.superclass.applyConfig.call(this);
19039         this.scroll = (this.config.scroll !== false);
19040     },
19041
19042     /*
19043      * Event that fires prior to the onMouseDown event.  Overrides
19044      * Roo.dd.DragDrop.
19045      */
19046     b4MouseDown: function(e) {
19047         // this.resetConstraints();
19048         this.autoOffset(e.getPageX(),
19049                             e.getPageY());
19050     },
19051
19052     /*
19053      * Event that fires prior to the onDrag event.  Overrides
19054      * Roo.dd.DragDrop.
19055      */
19056     b4Drag: function(e) {
19057         this.setDragElPos(e.getPageX(),
19058                             e.getPageY());
19059     },
19060
19061     toString: function() {
19062         return ("DD " + this.id);
19063     }
19064
19065     //////////////////////////////////////////////////////////////////////////
19066     // Debugging ygDragDrop events that can be overridden
19067     //////////////////////////////////////////////////////////////////////////
19068     /*
19069     startDrag: function(x, y) {
19070     },
19071
19072     onDrag: function(e) {
19073     },
19074
19075     onDragEnter: function(e, id) {
19076     },
19077
19078     onDragOver: function(e, id) {
19079     },
19080
19081     onDragOut: function(e, id) {
19082     },
19083
19084     onDragDrop: function(e, id) {
19085     },
19086
19087     endDrag: function(e) {
19088     }
19089
19090     */
19091
19092 });/*
19093  * Based on:
19094  * Ext JS Library 1.1.1
19095  * Copyright(c) 2006-2007, Ext JS, LLC.
19096  *
19097  * Originally Released Under LGPL - original licence link has changed is not relivant.
19098  *
19099  * Fork - LGPL
19100  * <script type="text/javascript">
19101  */
19102
19103 /**
19104  * @class Roo.dd.DDProxy
19105  * A DragDrop implementation that inserts an empty, bordered div into
19106  * the document that follows the cursor during drag operations.  At the time of
19107  * the click, the frame div is resized to the dimensions of the linked html
19108  * element, and moved to the exact location of the linked element.
19109  *
19110  * References to the "frame" element refer to the single proxy element that
19111  * was created to be dragged in place of all DDProxy elements on the
19112  * page.
19113  *
19114  * @extends Roo.dd.DD
19115  * @constructor
19116  * @param {String} id the id of the linked html element
19117  * @param {String} sGroup the group of related DragDrop objects
19118  * @param {object} config an object containing configurable attributes
19119  *                Valid properties for DDProxy in addition to those in DragDrop:
19120  *                   resizeFrame, centerFrame, dragElId
19121  */
19122 Roo.dd.DDProxy = function(id, sGroup, config) {
19123     if (id) {
19124         this.init(id, sGroup, config);
19125         this.initFrame();
19126     }
19127 };
19128
19129 /**
19130  * The default drag frame div id
19131  * @property Roo.dd.DDProxy.dragElId
19132  * @type String
19133  * @static
19134  */
19135 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19136
19137 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19138
19139     /**
19140      * By default we resize the drag frame to be the same size as the element
19141      * we want to drag (this is to get the frame effect).  We can turn it off
19142      * if we want a different behavior.
19143      * @property resizeFrame
19144      * @type boolean
19145      */
19146     resizeFrame: true,
19147
19148     /**
19149      * By default the frame is positioned exactly where the drag element is, so
19150      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19151      * you do not have constraints on the obj is to have the drag frame centered
19152      * around the cursor.  Set centerFrame to true for this effect.
19153      * @property centerFrame
19154      * @type boolean
19155      */
19156     centerFrame: false,
19157
19158     /**
19159      * Creates the proxy element if it does not yet exist
19160      * @method createFrame
19161      */
19162     createFrame: function() {
19163         var self = this;
19164         var body = document.body;
19165
19166         if (!body || !body.firstChild) {
19167             setTimeout( function() { self.createFrame(); }, 50 );
19168             return;
19169         }
19170
19171         var div = this.getDragEl();
19172
19173         if (!div) {
19174             div    = document.createElement("div");
19175             div.id = this.dragElId;
19176             var s  = div.style;
19177
19178             s.position   = "absolute";
19179             s.visibility = "hidden";
19180             s.cursor     = "move";
19181             s.border     = "2px solid #aaa";
19182             s.zIndex     = 999;
19183
19184             // appendChild can blow up IE if invoked prior to the window load event
19185             // while rendering a table.  It is possible there are other scenarios
19186             // that would cause this to happen as well.
19187             body.insertBefore(div, body.firstChild);
19188         }
19189     },
19190
19191     /**
19192      * Initialization for the drag frame element.  Must be called in the
19193      * constructor of all subclasses
19194      * @method initFrame
19195      */
19196     initFrame: function() {
19197         this.createFrame();
19198     },
19199
19200     applyConfig: function() {
19201         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19202
19203         this.resizeFrame = (this.config.resizeFrame !== false);
19204         this.centerFrame = (this.config.centerFrame);
19205         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19206     },
19207
19208     /**
19209      * Resizes the drag frame to the dimensions of the clicked object, positions
19210      * it over the object, and finally displays it
19211      * @method showFrame
19212      * @param {int} iPageX X click position
19213      * @param {int} iPageY Y click position
19214      * @private
19215      */
19216     showFrame: function(iPageX, iPageY) {
19217         var el = this.getEl();
19218         var dragEl = this.getDragEl();
19219         var s = dragEl.style;
19220
19221         this._resizeProxy();
19222
19223         if (this.centerFrame) {
19224             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19225                            Math.round(parseInt(s.height, 10)/2) );
19226         }
19227
19228         this.setDragElPos(iPageX, iPageY);
19229
19230         Roo.fly(dragEl).show();
19231     },
19232
19233     /**
19234      * The proxy is automatically resized to the dimensions of the linked
19235      * element when a drag is initiated, unless resizeFrame is set to false
19236      * @method _resizeProxy
19237      * @private
19238      */
19239     _resizeProxy: function() {
19240         if (this.resizeFrame) {
19241             var el = this.getEl();
19242             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19243         }
19244     },
19245
19246     // overrides Roo.dd.DragDrop
19247     b4MouseDown: function(e) {
19248         var x = e.getPageX();
19249         var y = e.getPageY();
19250         this.autoOffset(x, y);
19251         this.setDragElPos(x, y);
19252     },
19253
19254     // overrides Roo.dd.DragDrop
19255     b4StartDrag: function(x, y) {
19256         // show the drag frame
19257         this.showFrame(x, y);
19258     },
19259
19260     // overrides Roo.dd.DragDrop
19261     b4EndDrag: function(e) {
19262         Roo.fly(this.getDragEl()).hide();
19263     },
19264
19265     // overrides Roo.dd.DragDrop
19266     // By default we try to move the element to the last location of the frame.
19267     // This is so that the default behavior mirrors that of Roo.dd.DD.
19268     endDrag: function(e) {
19269
19270         var lel = this.getEl();
19271         var del = this.getDragEl();
19272
19273         // Show the drag frame briefly so we can get its position
19274         del.style.visibility = "";
19275
19276         this.beforeMove();
19277         // Hide the linked element before the move to get around a Safari
19278         // rendering bug.
19279         lel.style.visibility = "hidden";
19280         Roo.dd.DDM.moveToEl(lel, del);
19281         del.style.visibility = "hidden";
19282         lel.style.visibility = "";
19283
19284         this.afterDrag();
19285     },
19286
19287     beforeMove : function(){
19288
19289     },
19290
19291     afterDrag : function(){
19292
19293     },
19294
19295     toString: function() {
19296         return ("DDProxy " + this.id);
19297     }
19298
19299 });
19300 /*
19301  * Based on:
19302  * Ext JS Library 1.1.1
19303  * Copyright(c) 2006-2007, Ext JS, LLC.
19304  *
19305  * Originally Released Under LGPL - original licence link has changed is not relivant.
19306  *
19307  * Fork - LGPL
19308  * <script type="text/javascript">
19309  */
19310
19311  /**
19312  * @class Roo.dd.DDTarget
19313  * A DragDrop implementation that does not move, but can be a drop
19314  * target.  You would get the same result by simply omitting implementation
19315  * for the event callbacks, but this way we reduce the processing cost of the
19316  * event listener and the callbacks.
19317  * @extends Roo.dd.DragDrop
19318  * @constructor
19319  * @param {String} id the id of the element that is a drop target
19320  * @param {String} sGroup the group of related DragDrop objects
19321  * @param {object} config an object containing configurable attributes
19322  *                 Valid properties for DDTarget in addition to those in
19323  *                 DragDrop:
19324  *                    none
19325  */
19326 Roo.dd.DDTarget = function(id, sGroup, config) {
19327     if (id) {
19328         this.initTarget(id, sGroup, config);
19329     }
19330     if (config.listeners || config.events) { 
19331        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19332             listeners : config.listeners || {}, 
19333             events : config.events || {} 
19334         });    
19335     }
19336 };
19337
19338 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19339 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19340     toString: function() {
19341         return ("DDTarget " + this.id);
19342     }
19343 });
19344 /*
19345  * Based on:
19346  * Ext JS Library 1.1.1
19347  * Copyright(c) 2006-2007, Ext JS, LLC.
19348  *
19349  * Originally Released Under LGPL - original licence link has changed is not relivant.
19350  *
19351  * Fork - LGPL
19352  * <script type="text/javascript">
19353  */
19354  
19355
19356 /**
19357  * @class Roo.dd.ScrollManager
19358  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19359  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19360  * @singleton
19361  */
19362 Roo.dd.ScrollManager = function(){
19363     var ddm = Roo.dd.DragDropMgr;
19364     var els = {};
19365     var dragEl = null;
19366     var proc = {};
19367     
19368     
19369     
19370     var onStop = function(e){
19371         dragEl = null;
19372         clearProc();
19373     };
19374     
19375     var triggerRefresh = function(){
19376         if(ddm.dragCurrent){
19377              ddm.refreshCache(ddm.dragCurrent.groups);
19378         }
19379     };
19380     
19381     var doScroll = function(){
19382         if(ddm.dragCurrent){
19383             var dds = Roo.dd.ScrollManager;
19384             if(!dds.animate){
19385                 if(proc.el.scroll(proc.dir, dds.increment)){
19386                     triggerRefresh();
19387                 }
19388             }else{
19389                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19390             }
19391         }
19392     };
19393     
19394     var clearProc = function(){
19395         if(proc.id){
19396             clearInterval(proc.id);
19397         }
19398         proc.id = 0;
19399         proc.el = null;
19400         proc.dir = "";
19401     };
19402     
19403     var startProc = function(el, dir){
19404          Roo.log('scroll startproc');
19405         clearProc();
19406         proc.el = el;
19407         proc.dir = dir;
19408         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19409     };
19410     
19411     var onFire = function(e, isDrop){
19412        
19413         if(isDrop || !ddm.dragCurrent){ return; }
19414         var dds = Roo.dd.ScrollManager;
19415         if(!dragEl || dragEl != ddm.dragCurrent){
19416             dragEl = ddm.dragCurrent;
19417             // refresh regions on drag start
19418             dds.refreshCache();
19419         }
19420         
19421         var xy = Roo.lib.Event.getXY(e);
19422         var pt = new Roo.lib.Point(xy[0], xy[1]);
19423         for(var id in els){
19424             var el = els[id], r = el._region;
19425             if(r && r.contains(pt) && el.isScrollable()){
19426                 if(r.bottom - pt.y <= dds.thresh){
19427                     if(proc.el != el){
19428                         startProc(el, "down");
19429                     }
19430                     return;
19431                 }else if(r.right - pt.x <= dds.thresh){
19432                     if(proc.el != el){
19433                         startProc(el, "left");
19434                     }
19435                     return;
19436                 }else if(pt.y - r.top <= dds.thresh){
19437                     if(proc.el != el){
19438                         startProc(el, "up");
19439                     }
19440                     return;
19441                 }else if(pt.x - r.left <= dds.thresh){
19442                     if(proc.el != el){
19443                         startProc(el, "right");
19444                     }
19445                     return;
19446                 }
19447             }
19448         }
19449         clearProc();
19450     };
19451     
19452     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19453     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19454     
19455     return {
19456         /**
19457          * Registers new overflow element(s) to auto scroll
19458          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19459          */
19460         register : function(el){
19461             if(el instanceof Array){
19462                 for(var i = 0, len = el.length; i < len; i++) {
19463                         this.register(el[i]);
19464                 }
19465             }else{
19466                 el = Roo.get(el);
19467                 els[el.id] = el;
19468             }
19469             Roo.dd.ScrollManager.els = els;
19470         },
19471         
19472         /**
19473          * Unregisters overflow element(s) so they are no longer scrolled
19474          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19475          */
19476         unregister : function(el){
19477             if(el instanceof Array){
19478                 for(var i = 0, len = el.length; i < len; i++) {
19479                         this.unregister(el[i]);
19480                 }
19481             }else{
19482                 el = Roo.get(el);
19483                 delete els[el.id];
19484             }
19485         },
19486         
19487         /**
19488          * The number of pixels from the edge of a container the pointer needs to be to 
19489          * trigger scrolling (defaults to 25)
19490          * @type Number
19491          */
19492         thresh : 25,
19493         
19494         /**
19495          * The number of pixels to scroll in each scroll increment (defaults to 50)
19496          * @type Number
19497          */
19498         increment : 100,
19499         
19500         /**
19501          * The frequency of scrolls in milliseconds (defaults to 500)
19502          * @type Number
19503          */
19504         frequency : 500,
19505         
19506         /**
19507          * True to animate the scroll (defaults to true)
19508          * @type Boolean
19509          */
19510         animate: true,
19511         
19512         /**
19513          * The animation duration in seconds - 
19514          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19515          * @type Number
19516          */
19517         animDuration: .4,
19518         
19519         /**
19520          * Manually trigger a cache refresh.
19521          */
19522         refreshCache : function(){
19523             for(var id in els){
19524                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19525                     els[id]._region = els[id].getRegion();
19526                 }
19527             }
19528         }
19529     };
19530 }();/*
19531  * Based on:
19532  * Ext JS Library 1.1.1
19533  * Copyright(c) 2006-2007, Ext JS, LLC.
19534  *
19535  * Originally Released Under LGPL - original licence link has changed is not relivant.
19536  *
19537  * Fork - LGPL
19538  * <script type="text/javascript">
19539  */
19540  
19541
19542 /**
19543  * @class Roo.dd.Registry
19544  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19545  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19546  * @singleton
19547  */
19548 Roo.dd.Registry = function(){
19549     var elements = {}; 
19550     var handles = {}; 
19551     var autoIdSeed = 0;
19552
19553     var getId = function(el, autogen){
19554         if(typeof el == "string"){
19555             return el;
19556         }
19557         var id = el.id;
19558         if(!id && autogen !== false){
19559             id = "roodd-" + (++autoIdSeed);
19560             el.id = id;
19561         }
19562         return id;
19563     };
19564     
19565     return {
19566     /**
19567      * Register a drag drop element
19568      * @param {String|HTMLElement} element The id or DOM node to register
19569      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19570      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19571      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19572      * populated in the data object (if applicable):
19573      * <pre>
19574 Value      Description<br />
19575 ---------  ------------------------------------------<br />
19576 handles    Array of DOM nodes that trigger dragging<br />
19577            for the element being registered<br />
19578 isHandle   True if the element passed in triggers<br />
19579            dragging itself, else false
19580 </pre>
19581      */
19582         register : function(el, data){
19583             data = data || {};
19584             if(typeof el == "string"){
19585                 el = document.getElementById(el);
19586             }
19587             data.ddel = el;
19588             elements[getId(el)] = data;
19589             if(data.isHandle !== false){
19590                 handles[data.ddel.id] = data;
19591             }
19592             if(data.handles){
19593                 var hs = data.handles;
19594                 for(var i = 0, len = hs.length; i < len; i++){
19595                         handles[getId(hs[i])] = data;
19596                 }
19597             }
19598         },
19599
19600     /**
19601      * Unregister a drag drop element
19602      * @param {String|HTMLElement}  element The id or DOM node to unregister
19603      */
19604         unregister : function(el){
19605             var id = getId(el, false);
19606             var data = elements[id];
19607             if(data){
19608                 delete elements[id];
19609                 if(data.handles){
19610                     var hs = data.handles;
19611                     for(var i = 0, len = hs.length; i < len; i++){
19612                         delete handles[getId(hs[i], false)];
19613                     }
19614                 }
19615             }
19616         },
19617
19618     /**
19619      * Returns the handle registered for a DOM Node by id
19620      * @param {String|HTMLElement} id The DOM node or id to look up
19621      * @return {Object} handle The custom handle data
19622      */
19623         getHandle : function(id){
19624             if(typeof id != "string"){ // must be element?
19625                 id = id.id;
19626             }
19627             return handles[id];
19628         },
19629
19630     /**
19631      * Returns the handle that is registered for the DOM node that is the target of the event
19632      * @param {Event} e The event
19633      * @return {Object} handle The custom handle data
19634      */
19635         getHandleFromEvent : function(e){
19636             var t = Roo.lib.Event.getTarget(e);
19637             return t ? handles[t.id] : null;
19638         },
19639
19640     /**
19641      * Returns a custom data object that is registered for a DOM node by id
19642      * @param {String|HTMLElement} id The DOM node or id to look up
19643      * @return {Object} data The custom data
19644      */
19645         getTarget : function(id){
19646             if(typeof id != "string"){ // must be element?
19647                 id = id.id;
19648             }
19649             return elements[id];
19650         },
19651
19652     /**
19653      * Returns a custom data object that is registered for the DOM node that is the target of the event
19654      * @param {Event} e The event
19655      * @return {Object} data The custom data
19656      */
19657         getTargetFromEvent : function(e){
19658             var t = Roo.lib.Event.getTarget(e);
19659             return t ? elements[t.id] || handles[t.id] : null;
19660         }
19661     };
19662 }();/*
19663  * Based on:
19664  * Ext JS Library 1.1.1
19665  * Copyright(c) 2006-2007, Ext JS, LLC.
19666  *
19667  * Originally Released Under LGPL - original licence link has changed is not relivant.
19668  *
19669  * Fork - LGPL
19670  * <script type="text/javascript">
19671  */
19672  
19673
19674 /**
19675  * @class Roo.dd.StatusProxy
19676  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19677  * default drag proxy used by all Roo.dd components.
19678  * @constructor
19679  * @param {Object} config
19680  */
19681 Roo.dd.StatusProxy = function(config){
19682     Roo.apply(this, config);
19683     this.id = this.id || Roo.id();
19684     this.el = new Roo.Layer({
19685         dh: {
19686             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19687                 {tag: "div", cls: "x-dd-drop-icon"},
19688                 {tag: "div", cls: "x-dd-drag-ghost"}
19689             ]
19690         }, 
19691         shadow: !config || config.shadow !== false
19692     });
19693     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19694     this.dropStatus = this.dropNotAllowed;
19695 };
19696
19697 Roo.dd.StatusProxy.prototype = {
19698     /**
19699      * @cfg {String} dropAllowed
19700      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19701      */
19702     dropAllowed : "x-dd-drop-ok",
19703     /**
19704      * @cfg {String} dropNotAllowed
19705      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19706      */
19707     dropNotAllowed : "x-dd-drop-nodrop",
19708
19709     /**
19710      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19711      * over the current target element.
19712      * @param {String} cssClass The css class for the new drop status indicator image
19713      */
19714     setStatus : function(cssClass){
19715         cssClass = cssClass || this.dropNotAllowed;
19716         if(this.dropStatus != cssClass){
19717             this.el.replaceClass(this.dropStatus, cssClass);
19718             this.dropStatus = cssClass;
19719         }
19720     },
19721
19722     /**
19723      * Resets the status indicator to the default dropNotAllowed value
19724      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19725      */
19726     reset : function(clearGhost){
19727         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19728         this.dropStatus = this.dropNotAllowed;
19729         if(clearGhost){
19730             this.ghost.update("");
19731         }
19732     },
19733
19734     /**
19735      * Updates the contents of the ghost element
19736      * @param {String} html The html that will replace the current innerHTML of the ghost element
19737      */
19738     update : function(html){
19739         if(typeof html == "string"){
19740             this.ghost.update(html);
19741         }else{
19742             this.ghost.update("");
19743             html.style.margin = "0";
19744             this.ghost.dom.appendChild(html);
19745         }
19746         // ensure float = none set?? cant remember why though.
19747         var el = this.ghost.dom.firstChild;
19748                 if(el){
19749                         Roo.fly(el).setStyle('float', 'none');
19750                 }
19751     },
19752     
19753     /**
19754      * Returns the underlying proxy {@link Roo.Layer}
19755      * @return {Roo.Layer} el
19756     */
19757     getEl : function(){
19758         return this.el;
19759     },
19760
19761     /**
19762      * Returns the ghost element
19763      * @return {Roo.Element} el
19764      */
19765     getGhost : function(){
19766         return this.ghost;
19767     },
19768
19769     /**
19770      * Hides the proxy
19771      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19772      */
19773     hide : function(clear){
19774         this.el.hide();
19775         if(clear){
19776             this.reset(true);
19777         }
19778     },
19779
19780     /**
19781      * Stops the repair animation if it's currently running
19782      */
19783     stop : function(){
19784         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19785             this.anim.stop();
19786         }
19787     },
19788
19789     /**
19790      * Displays this proxy
19791      */
19792     show : function(){
19793         this.el.show();
19794     },
19795
19796     /**
19797      * Force the Layer to sync its shadow and shim positions to the element
19798      */
19799     sync : function(){
19800         this.el.sync();
19801     },
19802
19803     /**
19804      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19805      * invalid drop operation by the item being dragged.
19806      * @param {Array} xy The XY position of the element ([x, y])
19807      * @param {Function} callback The function to call after the repair is complete
19808      * @param {Object} scope The scope in which to execute the callback
19809      */
19810     repair : function(xy, callback, scope){
19811         this.callback = callback;
19812         this.scope = scope;
19813         if(xy && this.animRepair !== false){
19814             this.el.addClass("x-dd-drag-repair");
19815             this.el.hideUnders(true);
19816             this.anim = this.el.shift({
19817                 duration: this.repairDuration || .5,
19818                 easing: 'easeOut',
19819                 xy: xy,
19820                 stopFx: true,
19821                 callback: this.afterRepair,
19822                 scope: this
19823             });
19824         }else{
19825             this.afterRepair();
19826         }
19827     },
19828
19829     // private
19830     afterRepair : function(){
19831         this.hide(true);
19832         if(typeof this.callback == "function"){
19833             this.callback.call(this.scope || this);
19834         }
19835         this.callback = null;
19836         this.scope = null;
19837     }
19838 };/*
19839  * Based on:
19840  * Ext JS Library 1.1.1
19841  * Copyright(c) 2006-2007, Ext JS, LLC.
19842  *
19843  * Originally Released Under LGPL - original licence link has changed is not relivant.
19844  *
19845  * Fork - LGPL
19846  * <script type="text/javascript">
19847  */
19848
19849 /**
19850  * @class Roo.dd.DragSource
19851  * @extends Roo.dd.DDProxy
19852  * A simple class that provides the basic implementation needed to make any element draggable.
19853  * @constructor
19854  * @param {String/HTMLElement/Element} el The container element
19855  * @param {Object} config
19856  */
19857 Roo.dd.DragSource = function(el, config){
19858     this.el = Roo.get(el);
19859     this.dragData = {};
19860     
19861     Roo.apply(this, config);
19862     
19863     if(!this.proxy){
19864         this.proxy = new Roo.dd.StatusProxy();
19865     }
19866
19867     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19868           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19869     
19870     this.dragging = false;
19871 };
19872
19873 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19874     /**
19875      * @cfg {String} dropAllowed
19876      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19877      */
19878     dropAllowed : "x-dd-drop-ok",
19879     /**
19880      * @cfg {String} dropNotAllowed
19881      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19882      */
19883     dropNotAllowed : "x-dd-drop-nodrop",
19884
19885     /**
19886      * Returns the data object associated with this drag source
19887      * @return {Object} data An object containing arbitrary data
19888      */
19889     getDragData : function(e){
19890         return this.dragData;
19891     },
19892
19893     // private
19894     onDragEnter : function(e, id){
19895         var target = Roo.dd.DragDropMgr.getDDById(id);
19896         this.cachedTarget = target;
19897         if(this.beforeDragEnter(target, e, id) !== false){
19898             if(target.isNotifyTarget){
19899                 var status = target.notifyEnter(this, e, this.dragData);
19900                 this.proxy.setStatus(status);
19901             }else{
19902                 this.proxy.setStatus(this.dropAllowed);
19903             }
19904             
19905             if(this.afterDragEnter){
19906                 /**
19907                  * An empty function by default, but provided so that you can perform a custom action
19908                  * when the dragged item enters the drop target by providing an implementation.
19909                  * @param {Roo.dd.DragDrop} target The drop target
19910                  * @param {Event} e The event object
19911                  * @param {String} id The id of the dragged element
19912                  * @method afterDragEnter
19913                  */
19914                 this.afterDragEnter(target, e, id);
19915             }
19916         }
19917     },
19918
19919     /**
19920      * An empty function by default, but provided so that you can perform a custom action
19921      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19922      * @param {Roo.dd.DragDrop} target The drop target
19923      * @param {Event} e The event object
19924      * @param {String} id The id of the dragged element
19925      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19926      */
19927     beforeDragEnter : function(target, e, id){
19928         return true;
19929     },
19930
19931     // private
19932     alignElWithMouse: function() {
19933         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19934         this.proxy.sync();
19935     },
19936
19937     // private
19938     onDragOver : function(e, id){
19939         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19940         if(this.beforeDragOver(target, e, id) !== false){
19941             if(target.isNotifyTarget){
19942                 var status = target.notifyOver(this, e, this.dragData);
19943                 this.proxy.setStatus(status);
19944             }
19945
19946             if(this.afterDragOver){
19947                 /**
19948                  * An empty function by default, but provided so that you can perform a custom action
19949                  * while the dragged item is over the drop target by providing an implementation.
19950                  * @param {Roo.dd.DragDrop} target The drop target
19951                  * @param {Event} e The event object
19952                  * @param {String} id The id of the dragged element
19953                  * @method afterDragOver
19954                  */
19955                 this.afterDragOver(target, e, id);
19956             }
19957         }
19958     },
19959
19960     /**
19961      * An empty function by default, but provided so that you can perform a custom action
19962      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19963      * @param {Roo.dd.DragDrop} target The drop target
19964      * @param {Event} e The event object
19965      * @param {String} id The id of the dragged element
19966      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19967      */
19968     beforeDragOver : function(target, e, id){
19969         return true;
19970     },
19971
19972     // private
19973     onDragOut : function(e, id){
19974         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19975         if(this.beforeDragOut(target, e, id) !== false){
19976             if(target.isNotifyTarget){
19977                 target.notifyOut(this, e, this.dragData);
19978             }
19979             this.proxy.reset();
19980             if(this.afterDragOut){
19981                 /**
19982                  * An empty function by default, but provided so that you can perform a custom action
19983                  * after the dragged item is dragged out of the target without dropping.
19984                  * @param {Roo.dd.DragDrop} target The drop target
19985                  * @param {Event} e The event object
19986                  * @param {String} id The id of the dragged element
19987                  * @method afterDragOut
19988                  */
19989                 this.afterDragOut(target, e, id);
19990             }
19991         }
19992         this.cachedTarget = null;
19993     },
19994
19995     /**
19996      * An empty function by default, but provided so that you can perform a custom action before the dragged
19997      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
19998      * @param {Roo.dd.DragDrop} target The drop target
19999      * @param {Event} e The event object
20000      * @param {String} id The id of the dragged element
20001      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20002      */
20003     beforeDragOut : function(target, e, id){
20004         return true;
20005     },
20006     
20007     // private
20008     onDragDrop : function(e, id){
20009         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20010         if(this.beforeDragDrop(target, e, id) !== false){
20011             if(target.isNotifyTarget){
20012                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20013                     this.onValidDrop(target, e, id);
20014                 }else{
20015                     this.onInvalidDrop(target, e, id);
20016                 }
20017             }else{
20018                 this.onValidDrop(target, e, id);
20019             }
20020             
20021             if(this.afterDragDrop){
20022                 /**
20023                  * An empty function by default, but provided so that you can perform a custom action
20024                  * after a valid drag drop has occurred by providing an implementation.
20025                  * @param {Roo.dd.DragDrop} target The drop target
20026                  * @param {Event} e The event object
20027                  * @param {String} id The id of the dropped element
20028                  * @method afterDragDrop
20029                  */
20030                 this.afterDragDrop(target, e, id);
20031             }
20032         }
20033         delete this.cachedTarget;
20034     },
20035
20036     /**
20037      * An empty function by default, but provided so that you can perform a custom action before the dragged
20038      * item is dropped onto the target and optionally cancel the onDragDrop.
20039      * @param {Roo.dd.DragDrop} target The drop target
20040      * @param {Event} e The event object
20041      * @param {String} id The id of the dragged element
20042      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20043      */
20044     beforeDragDrop : function(target, e, id){
20045         return true;
20046     },
20047
20048     // private
20049     onValidDrop : function(target, e, id){
20050         this.hideProxy();
20051         if(this.afterValidDrop){
20052             /**
20053              * An empty function by default, but provided so that you can perform a custom action
20054              * after a valid drop has occurred by providing an implementation.
20055              * @param {Object} target The target DD 
20056              * @param {Event} e The event object
20057              * @param {String} id The id of the dropped element
20058              * @method afterInvalidDrop
20059              */
20060             this.afterValidDrop(target, e, id);
20061         }
20062     },
20063
20064     // private
20065     getRepairXY : function(e, data){
20066         return this.el.getXY();  
20067     },
20068
20069     // private
20070     onInvalidDrop : function(target, e, id){
20071         this.beforeInvalidDrop(target, e, id);
20072         if(this.cachedTarget){
20073             if(this.cachedTarget.isNotifyTarget){
20074                 this.cachedTarget.notifyOut(this, e, this.dragData);
20075             }
20076             this.cacheTarget = null;
20077         }
20078         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20079
20080         if(this.afterInvalidDrop){
20081             /**
20082              * An empty function by default, but provided so that you can perform a custom action
20083              * after an invalid drop has occurred by providing an implementation.
20084              * @param {Event} e The event object
20085              * @param {String} id The id of the dropped element
20086              * @method afterInvalidDrop
20087              */
20088             this.afterInvalidDrop(e, id);
20089         }
20090     },
20091
20092     // private
20093     afterRepair : function(){
20094         if(Roo.enableFx){
20095             this.el.highlight(this.hlColor || "c3daf9");
20096         }
20097         this.dragging = false;
20098     },
20099
20100     /**
20101      * An empty function by default, but provided so that you can perform a custom action after an invalid
20102      * drop has occurred.
20103      * @param {Roo.dd.DragDrop} target The drop target
20104      * @param {Event} e The event object
20105      * @param {String} id The id of the dragged element
20106      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20107      */
20108     beforeInvalidDrop : function(target, e, id){
20109         return true;
20110     },
20111
20112     // private
20113     handleMouseDown : function(e){
20114         if(this.dragging) {
20115             return;
20116         }
20117         var data = this.getDragData(e);
20118         if(data && this.onBeforeDrag(data, e) !== false){
20119             this.dragData = data;
20120             this.proxy.stop();
20121             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20122         } 
20123     },
20124
20125     /**
20126      * An empty function by default, but provided so that you can perform a custom action before the initial
20127      * drag event begins and optionally cancel it.
20128      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20129      * @param {Event} e The event object
20130      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20131      */
20132     onBeforeDrag : function(data, e){
20133         return true;
20134     },
20135
20136     /**
20137      * An empty function by default, but provided so that you can perform a custom action once the initial
20138      * drag event has begun.  The drag cannot be canceled from this function.
20139      * @param {Number} x The x position of the click on the dragged object
20140      * @param {Number} y The y position of the click on the dragged object
20141      */
20142     onStartDrag : Roo.emptyFn,
20143
20144     // private - YUI override
20145     startDrag : function(x, y){
20146         this.proxy.reset();
20147         this.dragging = true;
20148         this.proxy.update("");
20149         this.onInitDrag(x, y);
20150         this.proxy.show();
20151     },
20152
20153     // private
20154     onInitDrag : function(x, y){
20155         var clone = this.el.dom.cloneNode(true);
20156         clone.id = Roo.id(); // prevent duplicate ids
20157         this.proxy.update(clone);
20158         this.onStartDrag(x, y);
20159         return true;
20160     },
20161
20162     /**
20163      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20164      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20165      */
20166     getProxy : function(){
20167         return this.proxy;  
20168     },
20169
20170     /**
20171      * Hides the drag source's {@link Roo.dd.StatusProxy}
20172      */
20173     hideProxy : function(){
20174         this.proxy.hide();  
20175         this.proxy.reset(true);
20176         this.dragging = false;
20177     },
20178
20179     // private
20180     triggerCacheRefresh : function(){
20181         Roo.dd.DDM.refreshCache(this.groups);
20182     },
20183
20184     // private - override to prevent hiding
20185     b4EndDrag: function(e) {
20186     },
20187
20188     // private - override to prevent moving
20189     endDrag : function(e){
20190         this.onEndDrag(this.dragData, e);
20191     },
20192
20193     // private
20194     onEndDrag : function(data, e){
20195     },
20196     
20197     // private - pin to cursor
20198     autoOffset : function(x, y) {
20199         this.setDelta(-12, -20);
20200     }    
20201 });/*
20202  * Based on:
20203  * Ext JS Library 1.1.1
20204  * Copyright(c) 2006-2007, Ext JS, LLC.
20205  *
20206  * Originally Released Under LGPL - original licence link has changed is not relivant.
20207  *
20208  * Fork - LGPL
20209  * <script type="text/javascript">
20210  */
20211
20212
20213 /**
20214  * @class Roo.dd.DropTarget
20215  * @extends Roo.dd.DDTarget
20216  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20217  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20218  * @constructor
20219  * @param {String/HTMLElement/Element} el The container element
20220  * @param {Object} config
20221  */
20222 Roo.dd.DropTarget = function(el, config){
20223     this.el = Roo.get(el);
20224     
20225     var listeners = false; ;
20226     if (config && config.listeners) {
20227         listeners= config.listeners;
20228         delete config.listeners;
20229     }
20230     Roo.apply(this, config);
20231     
20232     if(this.containerScroll){
20233         Roo.dd.ScrollManager.register(this.el);
20234     }
20235     this.addEvents( {
20236          /**
20237          * @scope Roo.dd.DropTarget
20238          */
20239          
20240          /**
20241          * @event enter
20242          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20243          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20244          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20245          * 
20246          * IMPORTANT : it should set this.overClass and this.dropAllowed
20247          * 
20248          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20249          * @param {Event} e The event
20250          * @param {Object} data An object containing arbitrary data supplied by the drag source
20251          */
20252         "enter" : true,
20253         
20254          /**
20255          * @event over
20256          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20257          * This method will be called on every mouse movement while the drag source is over the drop target.
20258          * This default implementation simply returns the dropAllowed config value.
20259          * 
20260          * IMPORTANT : it should set this.dropAllowed
20261          * 
20262          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20263          * @param {Event} e The event
20264          * @param {Object} data An object containing arbitrary data supplied by the drag source
20265          
20266          */
20267         "over" : true,
20268         /**
20269          * @event out
20270          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20271          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20272          * overClass (if any) from the drop element.
20273          * 
20274          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20275          * @param {Event} e The event
20276          * @param {Object} data An object containing arbitrary data supplied by the drag source
20277          */
20278          "out" : true,
20279          
20280         /**
20281          * @event drop
20282          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20283          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20284          * implementation that does something to process the drop event and returns true so that the drag source's
20285          * repair action does not run.
20286          * 
20287          * IMPORTANT : it should set this.success
20288          * 
20289          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20290          * @param {Event} e The event
20291          * @param {Object} data An object containing arbitrary data supplied by the drag source
20292         */
20293          "drop" : true
20294     });
20295             
20296      
20297     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20298         this.el.dom, 
20299         this.ddGroup || this.group,
20300         {
20301             isTarget: true,
20302             listeners : listeners || {} 
20303            
20304         
20305         }
20306     );
20307
20308 };
20309
20310 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20311     /**
20312      * @cfg {String} overClass
20313      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20314      */
20315      /**
20316      * @cfg {String} ddGroup
20317      * The drag drop group to handle drop events for
20318      */
20319      
20320     /**
20321      * @cfg {String} dropAllowed
20322      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20323      */
20324     dropAllowed : "x-dd-drop-ok",
20325     /**
20326      * @cfg {String} dropNotAllowed
20327      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20328      */
20329     dropNotAllowed : "x-dd-drop-nodrop",
20330     /**
20331      * @cfg {boolean} success
20332      * set this after drop listener.. 
20333      */
20334     success : false,
20335     /**
20336      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20337      * if the drop point is valid for over/enter..
20338      */
20339     valid : false,
20340     // private
20341     isTarget : true,
20342
20343     // private
20344     isNotifyTarget : true,
20345     
20346     /**
20347      * @hide
20348      */
20349     notifyEnter : function(dd, e, data)
20350     {
20351         this.valid = true;
20352         this.fireEvent('enter', dd, e, data);
20353         if(this.overClass){
20354             this.el.addClass(this.overClass);
20355         }
20356         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20357             this.valid ? this.dropAllowed : this.dropNotAllowed
20358         );
20359     },
20360
20361     /**
20362      * @hide
20363      */
20364     notifyOver : function(dd, e, data)
20365     {
20366         this.valid = true;
20367         this.fireEvent('over', dd, e, data);
20368         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20369             this.valid ? this.dropAllowed : this.dropNotAllowed
20370         );
20371     },
20372
20373     /**
20374      * @hide
20375      */
20376     notifyOut : function(dd, e, data)
20377     {
20378         this.fireEvent('out', dd, e, data);
20379         if(this.overClass){
20380             this.el.removeClass(this.overClass);
20381         }
20382     },
20383
20384     /**
20385      * @hide
20386      */
20387     notifyDrop : function(dd, e, data)
20388     {
20389         this.success = false;
20390         this.fireEvent('drop', dd, e, data);
20391         return this.success;
20392     }
20393 });/*
20394  * Based on:
20395  * Ext JS Library 1.1.1
20396  * Copyright(c) 2006-2007, Ext JS, LLC.
20397  *
20398  * Originally Released Under LGPL - original licence link has changed is not relivant.
20399  *
20400  * Fork - LGPL
20401  * <script type="text/javascript">
20402  */
20403
20404
20405 /**
20406  * @class Roo.dd.DragZone
20407  * @extends Roo.dd.DragSource
20408  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20409  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20410  * @constructor
20411  * @param {String/HTMLElement/Element} el The container element
20412  * @param {Object} config
20413  */
20414 Roo.dd.DragZone = function(el, config){
20415     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20416     if(this.containerScroll){
20417         Roo.dd.ScrollManager.register(this.el);
20418     }
20419 };
20420
20421 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20422     /**
20423      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20424      * for auto scrolling during drag operations.
20425      */
20426     /**
20427      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20428      * method after a failed drop (defaults to "c3daf9" - light blue)
20429      */
20430
20431     /**
20432      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20433      * for a valid target to drag based on the mouse down. Override this method
20434      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20435      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20436      * @param {EventObject} e The mouse down event
20437      * @return {Object} The dragData
20438      */
20439     getDragData : function(e){
20440         return Roo.dd.Registry.getHandleFromEvent(e);
20441     },
20442     
20443     /**
20444      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20445      * this.dragData.ddel
20446      * @param {Number} x The x position of the click on the dragged object
20447      * @param {Number} y The y position of the click on the dragged object
20448      * @return {Boolean} true to continue the drag, false to cancel
20449      */
20450     onInitDrag : function(x, y){
20451         this.proxy.update(this.dragData.ddel.cloneNode(true));
20452         this.onStartDrag(x, y);
20453         return true;
20454     },
20455     
20456     /**
20457      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20458      */
20459     afterRepair : function(){
20460         if(Roo.enableFx){
20461             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20462         }
20463         this.dragging = false;
20464     },
20465
20466     /**
20467      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20468      * the XY of this.dragData.ddel
20469      * @param {EventObject} e The mouse up event
20470      * @return {Array} The xy location (e.g. [100, 200])
20471      */
20472     getRepairXY : function(e){
20473         return Roo.Element.fly(this.dragData.ddel).getXY();  
20474     }
20475 });/*
20476  * Based on:
20477  * Ext JS Library 1.1.1
20478  * Copyright(c) 2006-2007, Ext JS, LLC.
20479  *
20480  * Originally Released Under LGPL - original licence link has changed is not relivant.
20481  *
20482  * Fork - LGPL
20483  * <script type="text/javascript">
20484  */
20485 /**
20486  * @class Roo.dd.DropZone
20487  * @extends Roo.dd.DropTarget
20488  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20489  * By default, this class requires that child nodes accepting drop 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.DropZone = function(el, config){
20495     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20496 };
20497
20498 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20499     /**
20500      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20501      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20502      * provide your own custom lookup.
20503      * @param {Event} e The event
20504      * @return {Object} data The custom data
20505      */
20506     getTargetFromEvent : function(e){
20507         return Roo.dd.Registry.getTargetFromEvent(e);
20508     },
20509
20510     /**
20511      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20512      * that it has registered.  This method has no default implementation and should be overridden to provide
20513      * node-specific processing if necessary.
20514      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20515      * {@link #getTargetFromEvent} for this node)
20516      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20517      * @param {Event} e The event
20518      * @param {Object} data An object containing arbitrary data supplied by the drag source
20519      */
20520     onNodeEnter : function(n, dd, e, data){
20521         
20522     },
20523
20524     /**
20525      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20526      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20527      * overridden to provide the proper feedback.
20528      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20529      * {@link #getTargetFromEvent} for this node)
20530      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20531      * @param {Event} e The event
20532      * @param {Object} data An object containing arbitrary data supplied by the drag source
20533      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20534      * underlying {@link Roo.dd.StatusProxy} can be updated
20535      */
20536     onNodeOver : function(n, dd, e, data){
20537         return this.dropAllowed;
20538     },
20539
20540     /**
20541      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20542      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20543      * node-specific processing if necessary.
20544      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20545      * {@link #getTargetFromEvent} for this node)
20546      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20547      * @param {Event} e The event
20548      * @param {Object} data An object containing arbitrary data supplied by the drag source
20549      */
20550     onNodeOut : function(n, dd, e, data){
20551         
20552     },
20553
20554     /**
20555      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20556      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20557      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20558      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20559      * {@link #getTargetFromEvent} for this node)
20560      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20561      * @param {Event} e The event
20562      * @param {Object} data An object containing arbitrary data supplied by the drag source
20563      * @return {Boolean} True if the drop was valid, else false
20564      */
20565     onNodeDrop : function(n, dd, e, data){
20566         return false;
20567     },
20568
20569     /**
20570      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20571      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20572      * it should be overridden to provide the proper feedback if necessary.
20573      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20574      * @param {Event} e The event
20575      * @param {Object} data An object containing arbitrary data supplied by the drag source
20576      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20577      * underlying {@link Roo.dd.StatusProxy} can be updated
20578      */
20579     onContainerOver : function(dd, e, data){
20580         return this.dropNotAllowed;
20581     },
20582
20583     /**
20584      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20585      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20586      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20587      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20588      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20589      * @param {Event} e The event
20590      * @param {Object} data An object containing arbitrary data supplied by the drag source
20591      * @return {Boolean} True if the drop was valid, else false
20592      */
20593     onContainerDrop : function(dd, e, data){
20594         return false;
20595     },
20596
20597     /**
20598      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20599      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20600      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20601      * you should override this method and provide a custom implementation.
20602      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20603      * @param {Event} e The event
20604      * @param {Object} data An object containing arbitrary data supplied by the drag source
20605      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20606      * underlying {@link Roo.dd.StatusProxy} can be updated
20607      */
20608     notifyEnter : function(dd, e, data){
20609         return this.dropNotAllowed;
20610     },
20611
20612     /**
20613      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20614      * This method will be called on every mouse movement while the drag source is over the drop zone.
20615      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20616      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20617      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20618      * registered node, it will call {@link #onContainerOver}.
20619      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20620      * @param {Event} e The event
20621      * @param {Object} data An object containing arbitrary data supplied by the drag source
20622      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20623      * underlying {@link Roo.dd.StatusProxy} can be updated
20624      */
20625     notifyOver : function(dd, e, data){
20626         var n = this.getTargetFromEvent(e);
20627         if(!n){ // not over valid drop target
20628             if(this.lastOverNode){
20629                 this.onNodeOut(this.lastOverNode, dd, e, data);
20630                 this.lastOverNode = null;
20631             }
20632             return this.onContainerOver(dd, e, data);
20633         }
20634         if(this.lastOverNode != n){
20635             if(this.lastOverNode){
20636                 this.onNodeOut(this.lastOverNode, dd, e, data);
20637             }
20638             this.onNodeEnter(n, dd, e, data);
20639             this.lastOverNode = n;
20640         }
20641         return this.onNodeOver(n, dd, e, data);
20642     },
20643
20644     /**
20645      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20646      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20647      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20648      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20649      * @param {Event} e The event
20650      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20651      */
20652     notifyOut : function(dd, e, data){
20653         if(this.lastOverNode){
20654             this.onNodeOut(this.lastOverNode, dd, e, data);
20655             this.lastOverNode = null;
20656         }
20657     },
20658
20659     /**
20660      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20661      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20662      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20663      * otherwise it will call {@link #onContainerDrop}.
20664      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20665      * @param {Event} e The event
20666      * @param {Object} data An object containing arbitrary data supplied by the drag source
20667      * @return {Boolean} True if the drop was valid, else false
20668      */
20669     notifyDrop : function(dd, e, data){
20670         if(this.lastOverNode){
20671             this.onNodeOut(this.lastOverNode, dd, e, data);
20672             this.lastOverNode = null;
20673         }
20674         var n = this.getTargetFromEvent(e);
20675         return n ?
20676             this.onNodeDrop(n, dd, e, data) :
20677             this.onContainerDrop(dd, e, data);
20678     },
20679
20680     // private
20681     triggerCacheRefresh : function(){
20682         Roo.dd.DDM.refreshCache(this.groups);
20683     }  
20684 });/*
20685  * Based on:
20686  * Ext JS Library 1.1.1
20687  * Copyright(c) 2006-2007, Ext JS, LLC.
20688  *
20689  * Originally Released Under LGPL - original licence link has changed is not relivant.
20690  *
20691  * Fork - LGPL
20692  * <script type="text/javascript">
20693  */
20694
20695
20696 /**
20697  * @class Roo.data.SortTypes
20698  * @singleton
20699  * Defines the default sorting (casting?) comparison functions used when sorting data.
20700  */
20701 Roo.data.SortTypes = {
20702     /**
20703      * Default sort that does nothing
20704      * @param {Mixed} s The value being converted
20705      * @return {Mixed} The comparison value
20706      */
20707     none : function(s){
20708         return s;
20709     },
20710     
20711     /**
20712      * The regular expression used to strip tags
20713      * @type {RegExp}
20714      * @property
20715      */
20716     stripTagsRE : /<\/?[^>]+>/gi,
20717     
20718     /**
20719      * Strips all HTML tags to sort on text only
20720      * @param {Mixed} s The value being converted
20721      * @return {String} The comparison value
20722      */
20723     asText : function(s){
20724         return String(s).replace(this.stripTagsRE, "");
20725     },
20726     
20727     /**
20728      * Strips all HTML tags to sort on text only - Case insensitive
20729      * @param {Mixed} s The value being converted
20730      * @return {String} The comparison value
20731      */
20732     asUCText : function(s){
20733         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20734     },
20735     
20736     /**
20737      * Case insensitive string
20738      * @param {Mixed} s The value being converted
20739      * @return {String} The comparison value
20740      */
20741     asUCString : function(s) {
20742         return String(s).toUpperCase();
20743     },
20744     
20745     /**
20746      * Date sorting
20747      * @param {Mixed} s The value being converted
20748      * @return {Number} The comparison value
20749      */
20750     asDate : function(s) {
20751         if(!s){
20752             return 0;
20753         }
20754         if(s instanceof Date){
20755             return s.getTime();
20756         }
20757         return Date.parse(String(s));
20758     },
20759     
20760     /**
20761      * Float sorting
20762      * @param {Mixed} s The value being converted
20763      * @return {Float} The comparison value
20764      */
20765     asFloat : function(s) {
20766         var val = parseFloat(String(s).replace(/,/g, ""));
20767         if(isNaN(val)) val = 0;
20768         return val;
20769     },
20770     
20771     /**
20772      * Integer sorting
20773      * @param {Mixed} s The value being converted
20774      * @return {Number} The comparison value
20775      */
20776     asInt : function(s) {
20777         var val = parseInt(String(s).replace(/,/g, ""));
20778         if(isNaN(val)) val = 0;
20779         return val;
20780     }
20781 };/*
20782  * Based on:
20783  * Ext JS Library 1.1.1
20784  * Copyright(c) 2006-2007, Ext JS, LLC.
20785  *
20786  * Originally Released Under LGPL - original licence link has changed is not relivant.
20787  *
20788  * Fork - LGPL
20789  * <script type="text/javascript">
20790  */
20791
20792 /**
20793 * @class Roo.data.Record
20794  * Instances of this class encapsulate both record <em>definition</em> information, and record
20795  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20796  * to access Records cached in an {@link Roo.data.Store} object.<br>
20797  * <p>
20798  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20799  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20800  * objects.<br>
20801  * <p>
20802  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20803  * @constructor
20804  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20805  * {@link #create}. The parameters are the same.
20806  * @param {Array} data An associative Array of data values keyed by the field name.
20807  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20808  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20809  * not specified an integer id is generated.
20810  */
20811 Roo.data.Record = function(data, id){
20812     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20813     this.data = data;
20814 };
20815
20816 /**
20817  * Generate a constructor for a specific record layout.
20818  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20819  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20820  * Each field definition object may contain the following properties: <ul>
20821  * <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,
20822  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20823  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20824  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20825  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20826  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20827  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20828  * this may be omitted.</p></li>
20829  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20830  * <ul><li>auto (Default, implies no conversion)</li>
20831  * <li>string</li>
20832  * <li>int</li>
20833  * <li>float</li>
20834  * <li>boolean</li>
20835  * <li>date</li></ul></p></li>
20836  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20837  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20838  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20839  * by the Reader into an object that will be stored in the Record. It is passed the
20840  * following parameters:<ul>
20841  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20842  * </ul></p></li>
20843  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20844  * </ul>
20845  * <br>usage:<br><pre><code>
20846 var TopicRecord = Roo.data.Record.create(
20847     {name: 'title', mapping: 'topic_title'},
20848     {name: 'author', mapping: 'username'},
20849     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20850     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20851     {name: 'lastPoster', mapping: 'user2'},
20852     {name: 'excerpt', mapping: 'post_text'}
20853 );
20854
20855 var myNewRecord = new TopicRecord({
20856     title: 'Do my job please',
20857     author: 'noobie',
20858     totalPosts: 1,
20859     lastPost: new Date(),
20860     lastPoster: 'Animal',
20861     excerpt: 'No way dude!'
20862 });
20863 myStore.add(myNewRecord);
20864 </code></pre>
20865  * @method create
20866  * @static
20867  */
20868 Roo.data.Record.create = function(o){
20869     var f = function(){
20870         f.superclass.constructor.apply(this, arguments);
20871     };
20872     Roo.extend(f, Roo.data.Record);
20873     var p = f.prototype;
20874     p.fields = new Roo.util.MixedCollection(false, function(field){
20875         return field.name;
20876     });
20877     for(var i = 0, len = o.length; i < len; i++){
20878         p.fields.add(new Roo.data.Field(o[i]));
20879     }
20880     f.getField = function(name){
20881         return p.fields.get(name);  
20882     };
20883     return f;
20884 };
20885
20886 Roo.data.Record.AUTO_ID = 1000;
20887 Roo.data.Record.EDIT = 'edit';
20888 Roo.data.Record.REJECT = 'reject';
20889 Roo.data.Record.COMMIT = 'commit';
20890
20891 Roo.data.Record.prototype = {
20892     /**
20893      * Readonly flag - true if this record has been modified.
20894      * @type Boolean
20895      */
20896     dirty : false,
20897     editing : false,
20898     error: null,
20899     modified: null,
20900
20901     // private
20902     join : function(store){
20903         this.store = store;
20904     },
20905
20906     /**
20907      * Set the named field to the specified value.
20908      * @param {String} name The name of the field to set.
20909      * @param {Object} value The value to set the field to.
20910      */
20911     set : function(name, value){
20912         if(this.data[name] == value){
20913             return;
20914         }
20915         this.dirty = true;
20916         if(!this.modified){
20917             this.modified = {};
20918         }
20919         if(typeof this.modified[name] == 'undefined'){
20920             this.modified[name] = this.data[name];
20921         }
20922         this.data[name] = value;
20923         if(!this.editing && this.store){
20924             this.store.afterEdit(this);
20925         }       
20926     },
20927
20928     /**
20929      * Get the value of the named field.
20930      * @param {String} name The name of the field to get the value of.
20931      * @return {Object} The value of the field.
20932      */
20933     get : function(name){
20934         return this.data[name]; 
20935     },
20936
20937     // private
20938     beginEdit : function(){
20939         this.editing = true;
20940         this.modified = {}; 
20941     },
20942
20943     // private
20944     cancelEdit : function(){
20945         this.editing = false;
20946         delete this.modified;
20947     },
20948
20949     // private
20950     endEdit : function(){
20951         this.editing = false;
20952         if(this.dirty && this.store){
20953             this.store.afterEdit(this);
20954         }
20955     },
20956
20957     /**
20958      * Usually called by the {@link Roo.data.Store} which owns the Record.
20959      * Rejects all changes made to the Record since either creation, or the last commit operation.
20960      * Modified fields are reverted to their original values.
20961      * <p>
20962      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20963      * of reject operations.
20964      */
20965     reject : function(){
20966         var m = this.modified;
20967         for(var n in m){
20968             if(typeof m[n] != "function"){
20969                 this.data[n] = m[n];
20970             }
20971         }
20972         this.dirty = false;
20973         delete this.modified;
20974         this.editing = false;
20975         if(this.store){
20976             this.store.afterReject(this);
20977         }
20978     },
20979
20980     /**
20981      * Usually called by the {@link Roo.data.Store} which owns the Record.
20982      * Commits all changes made to the Record since either creation, or the last commit operation.
20983      * <p>
20984      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20985      * of commit operations.
20986      */
20987     commit : function(){
20988         this.dirty = false;
20989         delete this.modified;
20990         this.editing = false;
20991         if(this.store){
20992             this.store.afterCommit(this);
20993         }
20994     },
20995
20996     // private
20997     hasError : function(){
20998         return this.error != null;
20999     },
21000
21001     // private
21002     clearError : function(){
21003         this.error = null;
21004     },
21005
21006     /**
21007      * Creates a copy of this record.
21008      * @param {String} id (optional) A new record id if you don't want to use this record's id
21009      * @return {Record}
21010      */
21011     copy : function(newId) {
21012         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21013     }
21014 };/*
21015  * Based on:
21016  * Ext JS Library 1.1.1
21017  * Copyright(c) 2006-2007, Ext JS, LLC.
21018  *
21019  * Originally Released Under LGPL - original licence link has changed is not relivant.
21020  *
21021  * Fork - LGPL
21022  * <script type="text/javascript">
21023  */
21024
21025
21026
21027 /**
21028  * @class Roo.data.Store
21029  * @extends Roo.util.Observable
21030  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21031  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21032  * <p>
21033  * 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
21034  * has no knowledge of the format of the data returned by the Proxy.<br>
21035  * <p>
21036  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21037  * instances from the data object. These records are cached and made available through accessor functions.
21038  * @constructor
21039  * Creates a new Store.
21040  * @param {Object} config A config object containing the objects needed for the Store to access data,
21041  * and read the data into Records.
21042  */
21043 Roo.data.Store = function(config){
21044     this.data = new Roo.util.MixedCollection(false);
21045     this.data.getKey = function(o){
21046         return o.id;
21047     };
21048     this.baseParams = {};
21049     // private
21050     this.paramNames = {
21051         "start" : "start",
21052         "limit" : "limit",
21053         "sort" : "sort",
21054         "dir" : "dir",
21055         "multisort" : "_multisort"
21056     };
21057
21058     if(config && config.data){
21059         this.inlineData = config.data;
21060         delete config.data;
21061     }
21062
21063     Roo.apply(this, config);
21064     
21065     if(this.reader){ // reader passed
21066         this.reader = Roo.factory(this.reader, Roo.data);
21067         this.reader.xmodule = this.xmodule || false;
21068         if(!this.recordType){
21069             this.recordType = this.reader.recordType;
21070         }
21071         if(this.reader.onMetaChange){
21072             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21073         }
21074     }
21075
21076     if(this.recordType){
21077         this.fields = this.recordType.prototype.fields;
21078     }
21079     this.modified = [];
21080
21081     this.addEvents({
21082         /**
21083          * @event datachanged
21084          * Fires when the data cache has changed, and a widget which is using this Store
21085          * as a Record cache should refresh its view.
21086          * @param {Store} this
21087          */
21088         datachanged : true,
21089         /**
21090          * @event metachange
21091          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21092          * @param {Store} this
21093          * @param {Object} meta The JSON metadata
21094          */
21095         metachange : true,
21096         /**
21097          * @event add
21098          * Fires when Records have been added to the Store
21099          * @param {Store} this
21100          * @param {Roo.data.Record[]} records The array of Records added
21101          * @param {Number} index The index at which the record(s) were added
21102          */
21103         add : true,
21104         /**
21105          * @event remove
21106          * Fires when a Record has been removed from the Store
21107          * @param {Store} this
21108          * @param {Roo.data.Record} record The Record that was removed
21109          * @param {Number} index The index at which the record was removed
21110          */
21111         remove : true,
21112         /**
21113          * @event update
21114          * Fires when a Record has been updated
21115          * @param {Store} this
21116          * @param {Roo.data.Record} record The Record that was updated
21117          * @param {String} operation The update operation being performed.  Value may be one of:
21118          * <pre><code>
21119  Roo.data.Record.EDIT
21120  Roo.data.Record.REJECT
21121  Roo.data.Record.COMMIT
21122          * </code></pre>
21123          */
21124         update : true,
21125         /**
21126          * @event clear
21127          * Fires when the data cache has been cleared.
21128          * @param {Store} this
21129          */
21130         clear : true,
21131         /**
21132          * @event beforeload
21133          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21134          * the load action will be canceled.
21135          * @param {Store} this
21136          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21137          */
21138         beforeload : true,
21139         /**
21140          * @event beforeloadadd
21141          * Fires after a new set of Records has been loaded.
21142          * @param {Store} this
21143          * @param {Roo.data.Record[]} records The Records that were loaded
21144          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21145          */
21146         beforeloadadd : true,
21147         /**
21148          * @event load
21149          * Fires after a new set of Records has been loaded, before they are added to the store.
21150          * @param {Store} this
21151          * @param {Roo.data.Record[]} records The Records that were loaded
21152          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21153          * @params {Object} return from reader
21154          */
21155         load : true,
21156         /**
21157          * @event loadexception
21158          * Fires if an exception occurs in the Proxy during loading.
21159          * Called with the signature of the Proxy's "loadexception" event.
21160          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21161          * 
21162          * @param {Proxy} 
21163          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21164          * @param {Object} load options 
21165          * @param {Object} jsonData from your request (normally this contains the Exception)
21166          */
21167         loadexception : true
21168     });
21169     
21170     if(this.proxy){
21171         this.proxy = Roo.factory(this.proxy, Roo.data);
21172         this.proxy.xmodule = this.xmodule || false;
21173         this.relayEvents(this.proxy,  ["loadexception"]);
21174     }
21175     this.sortToggle = {};
21176     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21177
21178     Roo.data.Store.superclass.constructor.call(this);
21179
21180     if(this.inlineData){
21181         this.loadData(this.inlineData);
21182         delete this.inlineData;
21183     }
21184 };
21185
21186 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21187      /**
21188     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21189     * without a remote query - used by combo/forms at present.
21190     */
21191     
21192     /**
21193     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21194     */
21195     /**
21196     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21197     */
21198     /**
21199     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21200     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21201     */
21202     /**
21203     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21204     * on any HTTP request
21205     */
21206     /**
21207     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21208     */
21209     /**
21210     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21211     */
21212     multiSort: false,
21213     /**
21214     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21215     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21216     */
21217     remoteSort : false,
21218
21219     /**
21220     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21221      * loaded or when a record is removed. (defaults to false).
21222     */
21223     pruneModifiedRecords : false,
21224
21225     // private
21226     lastOptions : null,
21227
21228     /**
21229      * Add Records to the Store and fires the add event.
21230      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21231      */
21232     add : function(records){
21233         records = [].concat(records);
21234         for(var i = 0, len = records.length; i < len; i++){
21235             records[i].join(this);
21236         }
21237         var index = this.data.length;
21238         this.data.addAll(records);
21239         this.fireEvent("add", this, records, index);
21240     },
21241
21242     /**
21243      * Remove a Record from the Store and fires the remove event.
21244      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21245      */
21246     remove : function(record){
21247         var index = this.data.indexOf(record);
21248         this.data.removeAt(index);
21249         if(this.pruneModifiedRecords){
21250             this.modified.remove(record);
21251         }
21252         this.fireEvent("remove", this, record, index);
21253     },
21254
21255     /**
21256      * Remove all Records from the Store and fires the clear event.
21257      */
21258     removeAll : function(){
21259         this.data.clear();
21260         if(this.pruneModifiedRecords){
21261             this.modified = [];
21262         }
21263         this.fireEvent("clear", this);
21264     },
21265
21266     /**
21267      * Inserts Records to the Store at the given index and fires the add event.
21268      * @param {Number} index The start index at which to insert the passed Records.
21269      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21270      */
21271     insert : function(index, records){
21272         records = [].concat(records);
21273         for(var i = 0, len = records.length; i < len; i++){
21274             this.data.insert(index, records[i]);
21275             records[i].join(this);
21276         }
21277         this.fireEvent("add", this, records, index);
21278     },
21279
21280     /**
21281      * Get the index within the cache of the passed Record.
21282      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21283      * @return {Number} The index of the passed Record. Returns -1 if not found.
21284      */
21285     indexOf : function(record){
21286         return this.data.indexOf(record);
21287     },
21288
21289     /**
21290      * Get the index within the cache of the Record with the passed id.
21291      * @param {String} id The id of the Record to find.
21292      * @return {Number} The index of the Record. Returns -1 if not found.
21293      */
21294     indexOfId : function(id){
21295         return this.data.indexOfKey(id);
21296     },
21297
21298     /**
21299      * Get the Record with the specified id.
21300      * @param {String} id The id of the Record to find.
21301      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21302      */
21303     getById : function(id){
21304         return this.data.key(id);
21305     },
21306
21307     /**
21308      * Get the Record at the specified index.
21309      * @param {Number} index The index of the Record to find.
21310      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21311      */
21312     getAt : function(index){
21313         return this.data.itemAt(index);
21314     },
21315
21316     /**
21317      * Returns a range of Records between specified indices.
21318      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21319      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21320      * @return {Roo.data.Record[]} An array of Records
21321      */
21322     getRange : function(start, end){
21323         return this.data.getRange(start, end);
21324     },
21325
21326     // private
21327     storeOptions : function(o){
21328         o = Roo.apply({}, o);
21329         delete o.callback;
21330         delete o.scope;
21331         this.lastOptions = o;
21332     },
21333
21334     /**
21335      * Loads the Record cache from the configured Proxy using the configured Reader.
21336      * <p>
21337      * If using remote paging, then the first load call must specify the <em>start</em>
21338      * and <em>limit</em> properties in the options.params property to establish the initial
21339      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21340      * <p>
21341      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21342      * and this call will return before the new data has been loaded. Perform any post-processing
21343      * in a callback function, or in a "load" event handler.</strong>
21344      * <p>
21345      * @param {Object} options An object containing properties which control loading options:<ul>
21346      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21347      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21348      * passed the following arguments:<ul>
21349      * <li>r : Roo.data.Record[]</li>
21350      * <li>options: Options object from the load call</li>
21351      * <li>success: Boolean success indicator</li></ul></li>
21352      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21353      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21354      * </ul>
21355      */
21356     load : function(options){
21357         options = options || {};
21358         if(this.fireEvent("beforeload", this, options) !== false){
21359             this.storeOptions(options);
21360             var p = Roo.apply(options.params || {}, this.baseParams);
21361             // if meta was not loaded from remote source.. try requesting it.
21362             if (!this.reader.metaFromRemote) {
21363                 p._requestMeta = 1;
21364             }
21365             if(this.sortInfo && this.remoteSort){
21366                 var pn = this.paramNames;
21367                 p[pn["sort"]] = this.sortInfo.field;
21368                 p[pn["dir"]] = this.sortInfo.direction;
21369             }
21370             if (this.multiSort) {
21371                 var pn = this.paramNames;
21372                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21373             }
21374             
21375             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21376         }
21377     },
21378
21379     /**
21380      * Reloads the Record cache from the configured Proxy using the configured Reader and
21381      * the options from the last load operation performed.
21382      * @param {Object} options (optional) An object containing properties which may override the options
21383      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21384      * the most recently used options are reused).
21385      */
21386     reload : function(options){
21387         this.load(Roo.applyIf(options||{}, this.lastOptions));
21388     },
21389
21390     // private
21391     // Called as a callback by the Reader during a load operation.
21392     loadRecords : function(o, options, success){
21393         if(!o || success === false){
21394             if(success !== false){
21395                 this.fireEvent("load", this, [], options, o);
21396             }
21397             if(options.callback){
21398                 options.callback.call(options.scope || this, [], options, false);
21399             }
21400             return;
21401         }
21402         // if data returned failure - throw an exception.
21403         if (o.success === false) {
21404             // show a message if no listener is registered.
21405             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21406                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21407             }
21408             // loadmask wil be hooked into this..
21409             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21410             return;
21411         }
21412         var r = o.records, t = o.totalRecords || r.length;
21413         
21414         this.fireEvent("beforeloadadd", this, r, options, o);
21415         
21416         if(!options || options.add !== true){
21417             if(this.pruneModifiedRecords){
21418                 this.modified = [];
21419             }
21420             for(var i = 0, len = r.length; i < len; i++){
21421                 r[i].join(this);
21422             }
21423             if(this.snapshot){
21424                 this.data = this.snapshot;
21425                 delete this.snapshot;
21426             }
21427             this.data.clear();
21428             this.data.addAll(r);
21429             this.totalLength = t;
21430             this.applySort();
21431             this.fireEvent("datachanged", this);
21432         }else{
21433             this.totalLength = Math.max(t, this.data.length+r.length);
21434             this.add(r);
21435         }
21436         this.fireEvent("load", this, r, options, o);
21437         if(options.callback){
21438             options.callback.call(options.scope || this, r, options, true);
21439         }
21440     },
21441
21442
21443     /**
21444      * Loads data from a passed data block. A Reader which understands the format of the data
21445      * must have been configured in the constructor.
21446      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21447      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21448      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21449      */
21450     loadData : function(o, append){
21451         var r = this.reader.readRecords(o);
21452         this.loadRecords(r, {add: append}, true);
21453     },
21454
21455     /**
21456      * Gets the number of cached records.
21457      * <p>
21458      * <em>If using paging, this may not be the total size of the dataset. If the data object
21459      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21460      * the data set size</em>
21461      */
21462     getCount : function(){
21463         return this.data.length || 0;
21464     },
21465
21466     /**
21467      * Gets the total number of records in the dataset as returned by the server.
21468      * <p>
21469      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21470      * the dataset size</em>
21471      */
21472     getTotalCount : function(){
21473         return this.totalLength || 0;
21474     },
21475
21476     /**
21477      * Returns the sort state of the Store as an object with two properties:
21478      * <pre><code>
21479  field {String} The name of the field by which the Records are sorted
21480  direction {String} The sort order, "ASC" or "DESC"
21481      * </code></pre>
21482      */
21483     getSortState : function(){
21484         return this.sortInfo;
21485     },
21486
21487     // private
21488     applySort : function(){
21489         if(this.sortInfo && !this.remoteSort){
21490             var s = this.sortInfo, f = s.field;
21491             var st = this.fields.get(f).sortType;
21492             var fn = function(r1, r2){
21493                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21494                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21495             };
21496             this.data.sort(s.direction, fn);
21497             if(this.snapshot && this.snapshot != this.data){
21498                 this.snapshot.sort(s.direction, fn);
21499             }
21500         }
21501     },
21502
21503     /**
21504      * Sets the default sort column and order to be used by the next load operation.
21505      * @param {String} fieldName The name of the field to sort by.
21506      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21507      */
21508     setDefaultSort : function(field, dir){
21509         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21510     },
21511
21512     /**
21513      * Sort the Records.
21514      * If remote sorting is used, the sort is performed on the server, and the cache is
21515      * reloaded. If local sorting is used, the cache is sorted internally.
21516      * @param {String} fieldName The name of the field to sort by.
21517      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21518      */
21519     sort : function(fieldName, dir){
21520         var f = this.fields.get(fieldName);
21521         if(!dir){
21522             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21523             
21524             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21525                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21526             }else{
21527                 dir = f.sortDir;
21528             }
21529         }
21530         this.sortToggle[f.name] = dir;
21531         this.sortInfo = {field: f.name, direction: dir};
21532         if(!this.remoteSort){
21533             this.applySort();
21534             this.fireEvent("datachanged", this);
21535         }else{
21536             this.load(this.lastOptions);
21537         }
21538     },
21539
21540     /**
21541      * Calls the specified function for each of the Records in the cache.
21542      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21543      * Returning <em>false</em> aborts and exits the iteration.
21544      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21545      */
21546     each : function(fn, scope){
21547         this.data.each(fn, scope);
21548     },
21549
21550     /**
21551      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21552      * (e.g., during paging).
21553      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21554      */
21555     getModifiedRecords : function(){
21556         return this.modified;
21557     },
21558
21559     // private
21560     createFilterFn : function(property, value, anyMatch){
21561         if(!value.exec){ // not a regex
21562             value = String(value);
21563             if(value.length == 0){
21564                 return false;
21565             }
21566             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21567         }
21568         return function(r){
21569             return value.test(r.data[property]);
21570         };
21571     },
21572
21573     /**
21574      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21575      * @param {String} property A field on your records
21576      * @param {Number} start The record index to start at (defaults to 0)
21577      * @param {Number} end The last record index to include (defaults to length - 1)
21578      * @return {Number} The sum
21579      */
21580     sum : function(property, start, end){
21581         var rs = this.data.items, v = 0;
21582         start = start || 0;
21583         end = (end || end === 0) ? end : rs.length-1;
21584
21585         for(var i = start; i <= end; i++){
21586             v += (rs[i].data[property] || 0);
21587         }
21588         return v;
21589     },
21590
21591     /**
21592      * Filter the records by a specified property.
21593      * @param {String} field A field on your records
21594      * @param {String/RegExp} value Either a string that the field
21595      * should start with or a RegExp to test against the field
21596      * @param {Boolean} anyMatch True to match any part not just the beginning
21597      */
21598     filter : function(property, value, anyMatch){
21599         var fn = this.createFilterFn(property, value, anyMatch);
21600         return fn ? this.filterBy(fn) : this.clearFilter();
21601     },
21602
21603     /**
21604      * Filter by a function. The specified function will be called with each
21605      * record in this data source. If the function returns true the record is included,
21606      * otherwise it is filtered.
21607      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21608      * @param {Object} scope (optional) The scope of the function (defaults to this)
21609      */
21610     filterBy : function(fn, scope){
21611         this.snapshot = this.snapshot || this.data;
21612         this.data = this.queryBy(fn, scope||this);
21613         this.fireEvent("datachanged", this);
21614     },
21615
21616     /**
21617      * Query the records by a specified property.
21618      * @param {String} field A field on your records
21619      * @param {String/RegExp} value Either a string that the field
21620      * should start with or a RegExp to test against the field
21621      * @param {Boolean} anyMatch True to match any part not just the beginning
21622      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21623      */
21624     query : function(property, value, anyMatch){
21625         var fn = this.createFilterFn(property, value, anyMatch);
21626         return fn ? this.queryBy(fn) : this.data.clone();
21627     },
21628
21629     /**
21630      * Query by a function. The specified function will be called with each
21631      * record in this data source. If the function returns true the record is included
21632      * in the results.
21633      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21634      * @param {Object} scope (optional) The scope of the function (defaults to this)
21635       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21636      **/
21637     queryBy : function(fn, scope){
21638         var data = this.snapshot || this.data;
21639         return data.filterBy(fn, scope||this);
21640     },
21641
21642     /**
21643      * Collects unique values for a particular dataIndex from this store.
21644      * @param {String} dataIndex The property to collect
21645      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21646      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21647      * @return {Array} An array of the unique values
21648      **/
21649     collect : function(dataIndex, allowNull, bypassFilter){
21650         var d = (bypassFilter === true && this.snapshot) ?
21651                 this.snapshot.items : this.data.items;
21652         var v, sv, r = [], l = {};
21653         for(var i = 0, len = d.length; i < len; i++){
21654             v = d[i].data[dataIndex];
21655             sv = String(v);
21656             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21657                 l[sv] = true;
21658                 r[r.length] = v;
21659             }
21660         }
21661         return r;
21662     },
21663
21664     /**
21665      * Revert to a view of the Record cache with no filtering applied.
21666      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21667      */
21668     clearFilter : function(suppressEvent){
21669         if(this.snapshot && this.snapshot != this.data){
21670             this.data = this.snapshot;
21671             delete this.snapshot;
21672             if(suppressEvent !== true){
21673                 this.fireEvent("datachanged", this);
21674             }
21675         }
21676     },
21677
21678     // private
21679     afterEdit : function(record){
21680         if(this.modified.indexOf(record) == -1){
21681             this.modified.push(record);
21682         }
21683         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21684     },
21685     
21686     // private
21687     afterReject : function(record){
21688         this.modified.remove(record);
21689         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21690     },
21691
21692     // private
21693     afterCommit : function(record){
21694         this.modified.remove(record);
21695         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21696     },
21697
21698     /**
21699      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21700      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21701      */
21702     commitChanges : function(){
21703         var m = this.modified.slice(0);
21704         this.modified = [];
21705         for(var i = 0, len = m.length; i < len; i++){
21706             m[i].commit();
21707         }
21708     },
21709
21710     /**
21711      * Cancel outstanding changes on all changed records.
21712      */
21713     rejectChanges : function(){
21714         var m = this.modified.slice(0);
21715         this.modified = [];
21716         for(var i = 0, len = m.length; i < len; i++){
21717             m[i].reject();
21718         }
21719     },
21720
21721     onMetaChange : function(meta, rtype, o){
21722         this.recordType = rtype;
21723         this.fields = rtype.prototype.fields;
21724         delete this.snapshot;
21725         this.sortInfo = meta.sortInfo || this.sortInfo;
21726         this.modified = [];
21727         this.fireEvent('metachange', this, this.reader.meta);
21728     }
21729 });/*
21730  * Based on:
21731  * Ext JS Library 1.1.1
21732  * Copyright(c) 2006-2007, Ext JS, LLC.
21733  *
21734  * Originally Released Under LGPL - original licence link has changed is not relivant.
21735  *
21736  * Fork - LGPL
21737  * <script type="text/javascript">
21738  */
21739
21740 /**
21741  * @class Roo.data.SimpleStore
21742  * @extends Roo.data.Store
21743  * Small helper class to make creating Stores from Array data easier.
21744  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21745  * @cfg {Array} fields An array of field definition objects, or field name strings.
21746  * @cfg {Array} data The multi-dimensional array of data
21747  * @constructor
21748  * @param {Object} config
21749  */
21750 Roo.data.SimpleStore = function(config){
21751     Roo.data.SimpleStore.superclass.constructor.call(this, {
21752         isLocal : true,
21753         reader: new Roo.data.ArrayReader({
21754                 id: config.id
21755             },
21756             Roo.data.Record.create(config.fields)
21757         ),
21758         proxy : new Roo.data.MemoryProxy(config.data)
21759     });
21760     this.load();
21761 };
21762 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21763  * Based on:
21764  * Ext JS Library 1.1.1
21765  * Copyright(c) 2006-2007, Ext JS, LLC.
21766  *
21767  * Originally Released Under LGPL - original licence link has changed is not relivant.
21768  *
21769  * Fork - LGPL
21770  * <script type="text/javascript">
21771  */
21772
21773 /**
21774 /**
21775  * @extends Roo.data.Store
21776  * @class Roo.data.JsonStore
21777  * Small helper class to make creating Stores for JSON data easier. <br/>
21778 <pre><code>
21779 var store = new Roo.data.JsonStore({
21780     url: 'get-images.php',
21781     root: 'images',
21782     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21783 });
21784 </code></pre>
21785  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21786  * JsonReader and HttpProxy (unless inline data is provided).</b>
21787  * @cfg {Array} fields An array of field definition objects, or field name strings.
21788  * @constructor
21789  * @param {Object} config
21790  */
21791 Roo.data.JsonStore = function(c){
21792     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21793         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21794         reader: new Roo.data.JsonReader(c, c.fields)
21795     }));
21796 };
21797 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21798  * Based on:
21799  * Ext JS Library 1.1.1
21800  * Copyright(c) 2006-2007, Ext JS, LLC.
21801  *
21802  * Originally Released Under LGPL - original licence link has changed is not relivant.
21803  *
21804  * Fork - LGPL
21805  * <script type="text/javascript">
21806  */
21807
21808  
21809 Roo.data.Field = function(config){
21810     if(typeof config == "string"){
21811         config = {name: config};
21812     }
21813     Roo.apply(this, config);
21814     
21815     if(!this.type){
21816         this.type = "auto";
21817     }
21818     
21819     var st = Roo.data.SortTypes;
21820     // named sortTypes are supported, here we look them up
21821     if(typeof this.sortType == "string"){
21822         this.sortType = st[this.sortType];
21823     }
21824     
21825     // set default sortType for strings and dates
21826     if(!this.sortType){
21827         switch(this.type){
21828             case "string":
21829                 this.sortType = st.asUCString;
21830                 break;
21831             case "date":
21832                 this.sortType = st.asDate;
21833                 break;
21834             default:
21835                 this.sortType = st.none;
21836         }
21837     }
21838
21839     // define once
21840     var stripRe = /[\$,%]/g;
21841
21842     // prebuilt conversion function for this field, instead of
21843     // switching every time we're reading a value
21844     if(!this.convert){
21845         var cv, dateFormat = this.dateFormat;
21846         switch(this.type){
21847             case "":
21848             case "auto":
21849             case undefined:
21850                 cv = function(v){ return v; };
21851                 break;
21852             case "string":
21853                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21854                 break;
21855             case "int":
21856                 cv = function(v){
21857                     return v !== undefined && v !== null && v !== '' ?
21858                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21859                     };
21860                 break;
21861             case "float":
21862                 cv = function(v){
21863                     return v !== undefined && v !== null && v !== '' ?
21864                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21865                     };
21866                 break;
21867             case "bool":
21868             case "boolean":
21869                 cv = function(v){ return v === true || v === "true" || v == 1; };
21870                 break;
21871             case "date":
21872                 cv = function(v){
21873                     if(!v){
21874                         return '';
21875                     }
21876                     if(v instanceof Date){
21877                         return v;
21878                     }
21879                     if(dateFormat){
21880                         if(dateFormat == "timestamp"){
21881                             return new Date(v*1000);
21882                         }
21883                         return Date.parseDate(v, dateFormat);
21884                     }
21885                     var parsed = Date.parse(v);
21886                     return parsed ? new Date(parsed) : null;
21887                 };
21888              break;
21889             
21890         }
21891         this.convert = cv;
21892     }
21893 };
21894
21895 Roo.data.Field.prototype = {
21896     dateFormat: null,
21897     defaultValue: "",
21898     mapping: null,
21899     sortType : null,
21900     sortDir : "ASC"
21901 };/*
21902  * Based on:
21903  * Ext JS Library 1.1.1
21904  * Copyright(c) 2006-2007, Ext JS, LLC.
21905  *
21906  * Originally Released Under LGPL - original licence link has changed is not relivant.
21907  *
21908  * Fork - LGPL
21909  * <script type="text/javascript">
21910  */
21911  
21912 // Base class for reading structured data from a data source.  This class is intended to be
21913 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21914
21915 /**
21916  * @class Roo.data.DataReader
21917  * Base class for reading structured data from a data source.  This class is intended to be
21918  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21919  */
21920
21921 Roo.data.DataReader = function(meta, recordType){
21922     
21923     this.meta = meta;
21924     
21925     this.recordType = recordType instanceof Array ? 
21926         Roo.data.Record.create(recordType) : recordType;
21927 };
21928
21929 Roo.data.DataReader.prototype = {
21930      /**
21931      * Create an empty record
21932      * @param {Object} data (optional) - overlay some values
21933      * @return {Roo.data.Record} record created.
21934      */
21935     newRow :  function(d) {
21936         var da =  {};
21937         this.recordType.prototype.fields.each(function(c) {
21938             switch( c.type) {
21939                 case 'int' : da[c.name] = 0; break;
21940                 case 'date' : da[c.name] = new Date(); break;
21941                 case 'float' : da[c.name] = 0.0; break;
21942                 case 'boolean' : da[c.name] = false; break;
21943                 default : da[c.name] = ""; break;
21944             }
21945             
21946         });
21947         return new this.recordType(Roo.apply(da, d));
21948     }
21949     
21950 };/*
21951  * Based on:
21952  * Ext JS Library 1.1.1
21953  * Copyright(c) 2006-2007, Ext JS, LLC.
21954  *
21955  * Originally Released Under LGPL - original licence link has changed is not relivant.
21956  *
21957  * Fork - LGPL
21958  * <script type="text/javascript">
21959  */
21960
21961 /**
21962  * @class Roo.data.DataProxy
21963  * @extends Roo.data.Observable
21964  * This class is an abstract base class for implementations which provide retrieval of
21965  * unformatted data objects.<br>
21966  * <p>
21967  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21968  * (of the appropriate type which knows how to parse the data object) to provide a block of
21969  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21970  * <p>
21971  * Custom implementations must implement the load method as described in
21972  * {@link Roo.data.HttpProxy#load}.
21973  */
21974 Roo.data.DataProxy = function(){
21975     this.addEvents({
21976         /**
21977          * @event beforeload
21978          * Fires before a network request is made to retrieve a data object.
21979          * @param {Object} This DataProxy object.
21980          * @param {Object} params The params parameter to the load function.
21981          */
21982         beforeload : true,
21983         /**
21984          * @event load
21985          * Fires before the load method's callback is called.
21986          * @param {Object} This DataProxy object.
21987          * @param {Object} o The data object.
21988          * @param {Object} arg The callback argument object passed to the load function.
21989          */
21990         load : true,
21991         /**
21992          * @event loadexception
21993          * Fires if an Exception occurs during data retrieval.
21994          * @param {Object} This DataProxy object.
21995          * @param {Object} o The data object.
21996          * @param {Object} arg The callback argument object passed to the load function.
21997          * @param {Object} e The Exception.
21998          */
21999         loadexception : true
22000     });
22001     Roo.data.DataProxy.superclass.constructor.call(this);
22002 };
22003
22004 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22005
22006     /**
22007      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22008      */
22009 /*
22010  * Based on:
22011  * Ext JS Library 1.1.1
22012  * Copyright(c) 2006-2007, Ext JS, LLC.
22013  *
22014  * Originally Released Under LGPL - original licence link has changed is not relivant.
22015  *
22016  * Fork - LGPL
22017  * <script type="text/javascript">
22018  */
22019 /**
22020  * @class Roo.data.MemoryProxy
22021  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22022  * to the Reader when its load method is called.
22023  * @constructor
22024  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22025  */
22026 Roo.data.MemoryProxy = function(data){
22027     if (data.data) {
22028         data = data.data;
22029     }
22030     Roo.data.MemoryProxy.superclass.constructor.call(this);
22031     this.data = data;
22032 };
22033
22034 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22035     /**
22036      * Load data from the requested source (in this case an in-memory
22037      * data object passed to the constructor), read the data object into
22038      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22039      * process that block using the passed callback.
22040      * @param {Object} params This parameter is not used by the MemoryProxy class.
22041      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22042      * object into a block of Roo.data.Records.
22043      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22044      * The function must be passed <ul>
22045      * <li>The Record block object</li>
22046      * <li>The "arg" argument from the load function</li>
22047      * <li>A boolean success indicator</li>
22048      * </ul>
22049      * @param {Object} scope The scope in which to call the callback
22050      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22051      */
22052     load : function(params, reader, callback, scope, arg){
22053         params = params || {};
22054         var result;
22055         try {
22056             result = reader.readRecords(this.data);
22057         }catch(e){
22058             this.fireEvent("loadexception", this, arg, null, e);
22059             callback.call(scope, null, arg, false);
22060             return;
22061         }
22062         callback.call(scope, result, arg, true);
22063     },
22064     
22065     // private
22066     update : function(params, records){
22067         
22068     }
22069 });/*
22070  * Based on:
22071  * Ext JS Library 1.1.1
22072  * Copyright(c) 2006-2007, Ext JS, LLC.
22073  *
22074  * Originally Released Under LGPL - original licence link has changed is not relivant.
22075  *
22076  * Fork - LGPL
22077  * <script type="text/javascript">
22078  */
22079 /**
22080  * @class Roo.data.HttpProxy
22081  * @extends Roo.data.DataProxy
22082  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22083  * configured to reference a certain URL.<br><br>
22084  * <p>
22085  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22086  * from which the running page was served.<br><br>
22087  * <p>
22088  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22089  * <p>
22090  * Be aware that to enable the browser to parse an XML document, the server must set
22091  * the Content-Type header in the HTTP response to "text/xml".
22092  * @constructor
22093  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22094  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22095  * will be used to make the request.
22096  */
22097 Roo.data.HttpProxy = function(conn){
22098     Roo.data.HttpProxy.superclass.constructor.call(this);
22099     // is conn a conn config or a real conn?
22100     this.conn = conn;
22101     this.useAjax = !conn || !conn.events;
22102   
22103 };
22104
22105 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22106     // thse are take from connection...
22107     
22108     /**
22109      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22110      */
22111     /**
22112      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22113      * extra parameters to each request made by this object. (defaults to undefined)
22114      */
22115     /**
22116      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22117      *  to each request made by this object. (defaults to undefined)
22118      */
22119     /**
22120      * @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)
22121      */
22122     /**
22123      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22124      */
22125      /**
22126      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22127      * @type Boolean
22128      */
22129   
22130
22131     /**
22132      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22133      * @type Boolean
22134      */
22135     /**
22136      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22137      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22138      * a finer-grained basis than the DataProxy events.
22139      */
22140     getConnection : function(){
22141         return this.useAjax ? Roo.Ajax : this.conn;
22142     },
22143
22144     /**
22145      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22146      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22147      * process that block using the passed callback.
22148      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22149      * for the request to the remote server.
22150      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22151      * object into a block of Roo.data.Records.
22152      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22153      * The function must be passed <ul>
22154      * <li>The Record block object</li>
22155      * <li>The "arg" argument from the load function</li>
22156      * <li>A boolean success indicator</li>
22157      * </ul>
22158      * @param {Object} scope The scope in which to call the callback
22159      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22160      */
22161     load : function(params, reader, callback, scope, arg){
22162         if(this.fireEvent("beforeload", this, params) !== false){
22163             var  o = {
22164                 params : params || {},
22165                 request: {
22166                     callback : callback,
22167                     scope : scope,
22168                     arg : arg
22169                 },
22170                 reader: reader,
22171                 callback : this.loadResponse,
22172                 scope: this
22173             };
22174             if(this.useAjax){
22175                 Roo.applyIf(o, this.conn);
22176                 if(this.activeRequest){
22177                     Roo.Ajax.abort(this.activeRequest);
22178                 }
22179                 this.activeRequest = Roo.Ajax.request(o);
22180             }else{
22181                 this.conn.request(o);
22182             }
22183         }else{
22184             callback.call(scope||this, null, arg, false);
22185         }
22186     },
22187
22188     // private
22189     loadResponse : function(o, success, response){
22190         delete this.activeRequest;
22191         if(!success){
22192             this.fireEvent("loadexception", this, o, response);
22193             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22194             return;
22195         }
22196         var result;
22197         try {
22198             result = o.reader.read(response);
22199         }catch(e){
22200             this.fireEvent("loadexception", this, o, response, e);
22201             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22202             return;
22203         }
22204         
22205         this.fireEvent("load", this, o, o.request.arg);
22206         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22207     },
22208
22209     // private
22210     update : function(dataSet){
22211
22212     },
22213
22214     // private
22215     updateResponse : function(dataSet){
22216
22217     }
22218 });/*
22219  * Based on:
22220  * Ext JS Library 1.1.1
22221  * Copyright(c) 2006-2007, Ext JS, LLC.
22222  *
22223  * Originally Released Under LGPL - original licence link has changed is not relivant.
22224  *
22225  * Fork - LGPL
22226  * <script type="text/javascript">
22227  */
22228
22229 /**
22230  * @class Roo.data.ScriptTagProxy
22231  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22232  * other than the originating domain of the running page.<br><br>
22233  * <p>
22234  * <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
22235  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22236  * <p>
22237  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22238  * source code that is used as the source inside a &lt;script> tag.<br><br>
22239  * <p>
22240  * In order for the browser to process the returned data, the server must wrap the data object
22241  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22242  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22243  * depending on whether the callback name was passed:
22244  * <p>
22245  * <pre><code>
22246 boolean scriptTag = false;
22247 String cb = request.getParameter("callback");
22248 if (cb != null) {
22249     scriptTag = true;
22250     response.setContentType("text/javascript");
22251 } else {
22252     response.setContentType("application/x-json");
22253 }
22254 Writer out = response.getWriter();
22255 if (scriptTag) {
22256     out.write(cb + "(");
22257 }
22258 out.print(dataBlock.toJsonString());
22259 if (scriptTag) {
22260     out.write(");");
22261 }
22262 </pre></code>
22263  *
22264  * @constructor
22265  * @param {Object} config A configuration object.
22266  */
22267 Roo.data.ScriptTagProxy = function(config){
22268     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22269     Roo.apply(this, config);
22270     this.head = document.getElementsByTagName("head")[0];
22271 };
22272
22273 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22274
22275 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22276     /**
22277      * @cfg {String} url The URL from which to request the data object.
22278      */
22279     /**
22280      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22281      */
22282     timeout : 30000,
22283     /**
22284      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22285      * the server the name of the callback function set up by the load call to process the returned data object.
22286      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22287      * javascript output which calls this named function passing the data object as its only parameter.
22288      */
22289     callbackParam : "callback",
22290     /**
22291      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22292      * name to the request.
22293      */
22294     nocache : true,
22295
22296     /**
22297      * Load data from the configured URL, read the data object into
22298      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22299      * process that block using the passed callback.
22300      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22301      * for the request to the remote server.
22302      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22303      * object into a block of Roo.data.Records.
22304      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22305      * The function must be passed <ul>
22306      * <li>The Record block object</li>
22307      * <li>The "arg" argument from the load function</li>
22308      * <li>A boolean success indicator</li>
22309      * </ul>
22310      * @param {Object} scope The scope in which to call the callback
22311      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22312      */
22313     load : function(params, reader, callback, scope, arg){
22314         if(this.fireEvent("beforeload", this, params) !== false){
22315
22316             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22317
22318             var url = this.url;
22319             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22320             if(this.nocache){
22321                 url += "&_dc=" + (new Date().getTime());
22322             }
22323             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22324             var trans = {
22325                 id : transId,
22326                 cb : "stcCallback"+transId,
22327                 scriptId : "stcScript"+transId,
22328                 params : params,
22329                 arg : arg,
22330                 url : url,
22331                 callback : callback,
22332                 scope : scope,
22333                 reader : reader
22334             };
22335             var conn = this;
22336
22337             window[trans.cb] = function(o){
22338                 conn.handleResponse(o, trans);
22339             };
22340
22341             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22342
22343             if(this.autoAbort !== false){
22344                 this.abort();
22345             }
22346
22347             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22348
22349             var script = document.createElement("script");
22350             script.setAttribute("src", url);
22351             script.setAttribute("type", "text/javascript");
22352             script.setAttribute("id", trans.scriptId);
22353             this.head.appendChild(script);
22354
22355             this.trans = trans;
22356         }else{
22357             callback.call(scope||this, null, arg, false);
22358         }
22359     },
22360
22361     // private
22362     isLoading : function(){
22363         return this.trans ? true : false;
22364     },
22365
22366     /**
22367      * Abort the current server request.
22368      */
22369     abort : function(){
22370         if(this.isLoading()){
22371             this.destroyTrans(this.trans);
22372         }
22373     },
22374
22375     // private
22376     destroyTrans : function(trans, isLoaded){
22377         this.head.removeChild(document.getElementById(trans.scriptId));
22378         clearTimeout(trans.timeoutId);
22379         if(isLoaded){
22380             window[trans.cb] = undefined;
22381             try{
22382                 delete window[trans.cb];
22383             }catch(e){}
22384         }else{
22385             // if hasn't been loaded, wait for load to remove it to prevent script error
22386             window[trans.cb] = function(){
22387                 window[trans.cb] = undefined;
22388                 try{
22389                     delete window[trans.cb];
22390                 }catch(e){}
22391             };
22392         }
22393     },
22394
22395     // private
22396     handleResponse : function(o, trans){
22397         this.trans = false;
22398         this.destroyTrans(trans, true);
22399         var result;
22400         try {
22401             result = trans.reader.readRecords(o);
22402         }catch(e){
22403             this.fireEvent("loadexception", this, o, trans.arg, e);
22404             trans.callback.call(trans.scope||window, null, trans.arg, false);
22405             return;
22406         }
22407         this.fireEvent("load", this, o, trans.arg);
22408         trans.callback.call(trans.scope||window, result, trans.arg, true);
22409     },
22410
22411     // private
22412     handleFailure : function(trans){
22413         this.trans = false;
22414         this.destroyTrans(trans, false);
22415         this.fireEvent("loadexception", this, null, trans.arg);
22416         trans.callback.call(trans.scope||window, null, trans.arg, false);
22417     }
22418 });/*
22419  * Based on:
22420  * Ext JS Library 1.1.1
22421  * Copyright(c) 2006-2007, Ext JS, LLC.
22422  *
22423  * Originally Released Under LGPL - original licence link has changed is not relivant.
22424  *
22425  * Fork - LGPL
22426  * <script type="text/javascript">
22427  */
22428
22429 /**
22430  * @class Roo.data.JsonReader
22431  * @extends Roo.data.DataReader
22432  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22433  * based on mappings in a provided Roo.data.Record constructor.
22434  * 
22435  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22436  * in the reply previously. 
22437  * 
22438  * <p>
22439  * Example code:
22440  * <pre><code>
22441 var RecordDef = Roo.data.Record.create([
22442     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22443     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22444 ]);
22445 var myReader = new Roo.data.JsonReader({
22446     totalProperty: "results",    // The property which contains the total dataset size (optional)
22447     root: "rows",                // The property which contains an Array of row objects
22448     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22449 }, RecordDef);
22450 </code></pre>
22451  * <p>
22452  * This would consume a JSON file like this:
22453  * <pre><code>
22454 { 'results': 2, 'rows': [
22455     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22456     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22457 }
22458 </code></pre>
22459  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22460  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22461  * paged from the remote server.
22462  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22463  * @cfg {String} root name of the property which contains the Array of row objects.
22464  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22465  * @constructor
22466  * Create a new JsonReader
22467  * @param {Object} meta Metadata configuration options
22468  * @param {Object} recordType Either an Array of field definition objects,
22469  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22470  */
22471 Roo.data.JsonReader = function(meta, recordType){
22472     
22473     meta = meta || {};
22474     // set some defaults:
22475     Roo.applyIf(meta, {
22476         totalProperty: 'total',
22477         successProperty : 'success',
22478         root : 'data',
22479         id : 'id'
22480     });
22481     
22482     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22483 };
22484 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22485     
22486     /**
22487      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22488      * Used by Store query builder to append _requestMeta to params.
22489      * 
22490      */
22491     metaFromRemote : false,
22492     /**
22493      * This method is only used by a DataProxy which has retrieved data from a remote server.
22494      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22495      * @return {Object} data A data block which is used by an Roo.data.Store object as
22496      * a cache of Roo.data.Records.
22497      */
22498     read : function(response){
22499         var json = response.responseText;
22500        
22501         var o = /* eval:var:o */ eval("("+json+")");
22502         if(!o) {
22503             throw {message: "JsonReader.read: Json object not found"};
22504         }
22505         
22506         if(o.metaData){
22507             
22508             delete this.ef;
22509             this.metaFromRemote = true;
22510             this.meta = o.metaData;
22511             this.recordType = Roo.data.Record.create(o.metaData.fields);
22512             this.onMetaChange(this.meta, this.recordType, o);
22513         }
22514         return this.readRecords(o);
22515     },
22516
22517     // private function a store will implement
22518     onMetaChange : function(meta, recordType, o){
22519
22520     },
22521
22522     /**
22523          * @ignore
22524          */
22525     simpleAccess: function(obj, subsc) {
22526         return obj[subsc];
22527     },
22528
22529         /**
22530          * @ignore
22531          */
22532     getJsonAccessor: function(){
22533         var re = /[\[\.]/;
22534         return function(expr) {
22535             try {
22536                 return(re.test(expr))
22537                     ? new Function("obj", "return obj." + expr)
22538                     : function(obj){
22539                         return obj[expr];
22540                     };
22541             } catch(e){}
22542             return Roo.emptyFn;
22543         };
22544     }(),
22545
22546     /**
22547      * Create a data block containing Roo.data.Records from an XML document.
22548      * @param {Object} o An object which contains an Array of row objects in the property specified
22549      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22550      * which contains the total size of the dataset.
22551      * @return {Object} data A data block which is used by an Roo.data.Store object as
22552      * a cache of Roo.data.Records.
22553      */
22554     readRecords : function(o){
22555         /**
22556          * After any data loads, the raw JSON data is available for further custom processing.
22557          * @type Object
22558          */
22559         this.o = o;
22560         var s = this.meta, Record = this.recordType,
22561             f = Record.prototype.fields, fi = f.items, fl = f.length;
22562
22563 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22564         if (!this.ef) {
22565             if(s.totalProperty) {
22566                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22567                 }
22568                 if(s.successProperty) {
22569                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22570                 }
22571                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22572                 if (s.id) {
22573                         var g = this.getJsonAccessor(s.id);
22574                         this.getId = function(rec) {
22575                                 var r = g(rec);
22576                                 return (r === undefined || r === "") ? null : r;
22577                         };
22578                 } else {
22579                         this.getId = function(){return null;};
22580                 }
22581             this.ef = [];
22582             for(var jj = 0; jj < fl; jj++){
22583                 f = fi[jj];
22584                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22585                 this.ef[jj] = this.getJsonAccessor(map);
22586             }
22587         }
22588
22589         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22590         if(s.totalProperty){
22591             var vt = parseInt(this.getTotal(o), 10);
22592             if(!isNaN(vt)){
22593                 totalRecords = vt;
22594             }
22595         }
22596         if(s.successProperty){
22597             var vs = this.getSuccess(o);
22598             if(vs === false || vs === 'false'){
22599                 success = false;
22600             }
22601         }
22602         var records = [];
22603             for(var i = 0; i < c; i++){
22604                     var n = root[i];
22605                 var values = {};
22606                 var id = this.getId(n);
22607                 for(var j = 0; j < fl; j++){
22608                     f = fi[j];
22609                 var v = this.ef[j](n);
22610                 if (!f.convert) {
22611                     Roo.log('missing convert for ' + f.name);
22612                     Roo.log(f);
22613                     continue;
22614                 }
22615                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22616                 }
22617                 var record = new Record(values, id);
22618                 record.json = n;
22619                 records[i] = record;
22620             }
22621             return {
22622             raw : o,
22623                 success : success,
22624                 records : records,
22625                 totalRecords : totalRecords
22626             };
22627     }
22628 });/*
22629  * Based on:
22630  * Ext JS Library 1.1.1
22631  * Copyright(c) 2006-2007, Ext JS, LLC.
22632  *
22633  * Originally Released Under LGPL - original licence link has changed is not relivant.
22634  *
22635  * Fork - LGPL
22636  * <script type="text/javascript">
22637  */
22638
22639 /**
22640  * @class Roo.data.XmlReader
22641  * @extends Roo.data.DataReader
22642  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22643  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22644  * <p>
22645  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22646  * header in the HTTP response must be set to "text/xml".</em>
22647  * <p>
22648  * Example code:
22649  * <pre><code>
22650 var RecordDef = Roo.data.Record.create([
22651    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22652    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22653 ]);
22654 var myReader = new Roo.data.XmlReader({
22655    totalRecords: "results", // The element which contains the total dataset size (optional)
22656    record: "row",           // The repeated element which contains row information
22657    id: "id"                 // The element within the row that provides an ID for the record (optional)
22658 }, RecordDef);
22659 </code></pre>
22660  * <p>
22661  * This would consume an XML file like this:
22662  * <pre><code>
22663 &lt;?xml?>
22664 &lt;dataset>
22665  &lt;results>2&lt;/results>
22666  &lt;row>
22667    &lt;id>1&lt;/id>
22668    &lt;name>Bill&lt;/name>
22669    &lt;occupation>Gardener&lt;/occupation>
22670  &lt;/row>
22671  &lt;row>
22672    &lt;id>2&lt;/id>
22673    &lt;name>Ben&lt;/name>
22674    &lt;occupation>Horticulturalist&lt;/occupation>
22675  &lt;/row>
22676 &lt;/dataset>
22677 </code></pre>
22678  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22679  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22680  * paged from the remote server.
22681  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22682  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22683  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22684  * a record identifier value.
22685  * @constructor
22686  * Create a new XmlReader
22687  * @param {Object} meta Metadata configuration options
22688  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22689  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22690  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22691  */
22692 Roo.data.XmlReader = function(meta, recordType){
22693     meta = meta || {};
22694     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22695 };
22696 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22697     /**
22698      * This method is only used by a DataProxy which has retrieved data from a remote server.
22699          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22700          * to contain a method called 'responseXML' that returns an XML document object.
22701      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22702      * a cache of Roo.data.Records.
22703      */
22704     read : function(response){
22705         var doc = response.responseXML;
22706         if(!doc) {
22707             throw {message: "XmlReader.read: XML Document not available"};
22708         }
22709         return this.readRecords(doc);
22710     },
22711
22712     /**
22713      * Create a data block containing Roo.data.Records from an XML document.
22714          * @param {Object} doc A parsed XML document.
22715      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22716      * a cache of Roo.data.Records.
22717      */
22718     readRecords : function(doc){
22719         /**
22720          * After any data loads/reads, the raw XML Document is available for further custom processing.
22721          * @type XMLDocument
22722          */
22723         this.xmlData = doc;
22724         var root = doc.documentElement || doc;
22725         var q = Roo.DomQuery;
22726         var recordType = this.recordType, fields = recordType.prototype.fields;
22727         var sid = this.meta.id;
22728         var totalRecords = 0, success = true;
22729         if(this.meta.totalRecords){
22730             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22731         }
22732         
22733         if(this.meta.success){
22734             var sv = q.selectValue(this.meta.success, root, true);
22735             success = sv !== false && sv !== 'false';
22736         }
22737         var records = [];
22738         var ns = q.select(this.meta.record, root);
22739         for(var i = 0, len = ns.length; i < len; i++) {
22740                 var n = ns[i];
22741                 var values = {};
22742                 var id = sid ? q.selectValue(sid, n) : undefined;
22743                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22744                     var f = fields.items[j];
22745                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22746                     v = f.convert(v);
22747                     values[f.name] = v;
22748                 }
22749                 var record = new recordType(values, id);
22750                 record.node = n;
22751                 records[records.length] = record;
22752             }
22753
22754             return {
22755                 success : success,
22756                 records : records,
22757                 totalRecords : totalRecords || records.length
22758             };
22759     }
22760 });/*
22761  * Based on:
22762  * Ext JS Library 1.1.1
22763  * Copyright(c) 2006-2007, Ext JS, LLC.
22764  *
22765  * Originally Released Under LGPL - original licence link has changed is not relivant.
22766  *
22767  * Fork - LGPL
22768  * <script type="text/javascript">
22769  */
22770
22771 /**
22772  * @class Roo.data.ArrayReader
22773  * @extends Roo.data.DataReader
22774  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22775  * Each element of that Array represents a row of data fields. The
22776  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22777  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22778  * <p>
22779  * Example code:.
22780  * <pre><code>
22781 var RecordDef = Roo.data.Record.create([
22782     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22783     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22784 ]);
22785 var myReader = new Roo.data.ArrayReader({
22786     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22787 }, RecordDef);
22788 </code></pre>
22789  * <p>
22790  * This would consume an Array like this:
22791  * <pre><code>
22792 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22793   </code></pre>
22794  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22795  * @constructor
22796  * Create a new JsonReader
22797  * @param {Object} meta Metadata configuration options.
22798  * @param {Object} recordType Either an Array of field definition objects
22799  * as specified to {@link Roo.data.Record#create},
22800  * or an {@link Roo.data.Record} object
22801  * created using {@link Roo.data.Record#create}.
22802  */
22803 Roo.data.ArrayReader = function(meta, recordType){
22804     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22805 };
22806
22807 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22808     /**
22809      * Create a data block containing Roo.data.Records from an XML document.
22810      * @param {Object} o An Array of row objects which represents the dataset.
22811      * @return {Object} data A data block which is used by an Roo.data.Store object as
22812      * a cache of Roo.data.Records.
22813      */
22814     readRecords : function(o){
22815         var sid = this.meta ? this.meta.id : null;
22816         var recordType = this.recordType, fields = recordType.prototype.fields;
22817         var records = [];
22818         var root = o;
22819             for(var i = 0; i < root.length; i++){
22820                     var n = root[i];
22821                 var values = {};
22822                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22823                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22824                 var f = fields.items[j];
22825                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22826                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22827                 v = f.convert(v);
22828                 values[f.name] = v;
22829             }
22830                 var record = new recordType(values, id);
22831                 record.json = n;
22832                 records[records.length] = record;
22833             }
22834             return {
22835                 records : records,
22836                 totalRecords : records.length
22837             };
22838     }
22839 });/*
22840  * Based on:
22841  * Ext JS Library 1.1.1
22842  * Copyright(c) 2006-2007, Ext JS, LLC.
22843  *
22844  * Originally Released Under LGPL - original licence link has changed is not relivant.
22845  *
22846  * Fork - LGPL
22847  * <script type="text/javascript">
22848  */
22849
22850
22851 /**
22852  * @class Roo.data.Tree
22853  * @extends Roo.util.Observable
22854  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22855  * in the tree have most standard DOM functionality.
22856  * @constructor
22857  * @param {Node} root (optional) The root node
22858  */
22859 Roo.data.Tree = function(root){
22860    this.nodeHash = {};
22861    /**
22862     * The root node for this tree
22863     * @type Node
22864     */
22865    this.root = null;
22866    if(root){
22867        this.setRootNode(root);
22868    }
22869    this.addEvents({
22870        /**
22871         * @event append
22872         * Fires when a new child node is appended to a node in this tree.
22873         * @param {Tree} tree The owner tree
22874         * @param {Node} parent The parent node
22875         * @param {Node} node The newly appended node
22876         * @param {Number} index The index of the newly appended node
22877         */
22878        "append" : true,
22879        /**
22880         * @event remove
22881         * Fires when a child node is removed from a node in this tree.
22882         * @param {Tree} tree The owner tree
22883         * @param {Node} parent The parent node
22884         * @param {Node} node The child node removed
22885         */
22886        "remove" : true,
22887        /**
22888         * @event move
22889         * Fires when a node is moved to a new location in the tree
22890         * @param {Tree} tree The owner tree
22891         * @param {Node} node The node moved
22892         * @param {Node} oldParent The old parent of this node
22893         * @param {Node} newParent The new parent of this node
22894         * @param {Number} index The index it was moved to
22895         */
22896        "move" : true,
22897        /**
22898         * @event insert
22899         * Fires when a new child node is inserted in a node in this tree.
22900         * @param {Tree} tree The owner tree
22901         * @param {Node} parent The parent node
22902         * @param {Node} node The child node inserted
22903         * @param {Node} refNode The child node the node was inserted before
22904         */
22905        "insert" : true,
22906        /**
22907         * @event beforeappend
22908         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22909         * @param {Tree} tree The owner tree
22910         * @param {Node} parent The parent node
22911         * @param {Node} node The child node to be appended
22912         */
22913        "beforeappend" : true,
22914        /**
22915         * @event beforeremove
22916         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22917         * @param {Tree} tree The owner tree
22918         * @param {Node} parent The parent node
22919         * @param {Node} node The child node to be removed
22920         */
22921        "beforeremove" : true,
22922        /**
22923         * @event beforemove
22924         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22925         * @param {Tree} tree The owner tree
22926         * @param {Node} node The node being moved
22927         * @param {Node} oldParent The parent of the node
22928         * @param {Node} newParent The new parent the node is moving to
22929         * @param {Number} index The index it is being moved to
22930         */
22931        "beforemove" : true,
22932        /**
22933         * @event beforeinsert
22934         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22935         * @param {Tree} tree The owner tree
22936         * @param {Node} parent The parent node
22937         * @param {Node} node The child node to be inserted
22938         * @param {Node} refNode The child node the node is being inserted before
22939         */
22940        "beforeinsert" : true
22941    });
22942
22943     Roo.data.Tree.superclass.constructor.call(this);
22944 };
22945
22946 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22947     pathSeparator: "/",
22948
22949     proxyNodeEvent : function(){
22950         return this.fireEvent.apply(this, arguments);
22951     },
22952
22953     /**
22954      * Returns the root node for this tree.
22955      * @return {Node}
22956      */
22957     getRootNode : function(){
22958         return this.root;
22959     },
22960
22961     /**
22962      * Sets the root node for this tree.
22963      * @param {Node} node
22964      * @return {Node}
22965      */
22966     setRootNode : function(node){
22967         this.root = node;
22968         node.ownerTree = this;
22969         node.isRoot = true;
22970         this.registerNode(node);
22971         return node;
22972     },
22973
22974     /**
22975      * Gets a node in this tree by its id.
22976      * @param {String} id
22977      * @return {Node}
22978      */
22979     getNodeById : function(id){
22980         return this.nodeHash[id];
22981     },
22982
22983     registerNode : function(node){
22984         this.nodeHash[node.id] = node;
22985     },
22986
22987     unregisterNode : function(node){
22988         delete this.nodeHash[node.id];
22989     },
22990
22991     toString : function(){
22992         return "[Tree"+(this.id?" "+this.id:"")+"]";
22993     }
22994 });
22995
22996 /**
22997  * @class Roo.data.Node
22998  * @extends Roo.util.Observable
22999  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23000  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23001  * @constructor
23002  * @param {Object} attributes The attributes/config for the node
23003  */
23004 Roo.data.Node = function(attributes){
23005     /**
23006      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23007      * @type {Object}
23008      */
23009     this.attributes = attributes || {};
23010     this.leaf = this.attributes.leaf;
23011     /**
23012      * The node id. @type String
23013      */
23014     this.id = this.attributes.id;
23015     if(!this.id){
23016         this.id = Roo.id(null, "ynode-");
23017         this.attributes.id = this.id;
23018     }
23019      
23020     
23021     /**
23022      * All child nodes of this node. @type Array
23023      */
23024     this.childNodes = [];
23025     if(!this.childNodes.indexOf){ // indexOf is a must
23026         this.childNodes.indexOf = function(o){
23027             for(var i = 0, len = this.length; i < len; i++){
23028                 if(this[i] == o) {
23029                     return i;
23030                 }
23031             }
23032             return -1;
23033         };
23034     }
23035     /**
23036      * The parent node for this node. @type Node
23037      */
23038     this.parentNode = null;
23039     /**
23040      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23041      */
23042     this.firstChild = null;
23043     /**
23044      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23045      */
23046     this.lastChild = null;
23047     /**
23048      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23049      */
23050     this.previousSibling = null;
23051     /**
23052      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23053      */
23054     this.nextSibling = null;
23055
23056     this.addEvents({
23057        /**
23058         * @event append
23059         * Fires when a new child node is appended
23060         * @param {Tree} tree The owner tree
23061         * @param {Node} this This node
23062         * @param {Node} node The newly appended node
23063         * @param {Number} index The index of the newly appended node
23064         */
23065        "append" : true,
23066        /**
23067         * @event remove
23068         * Fires when a child node is removed
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} this This node
23071         * @param {Node} node The removed node
23072         */
23073        "remove" : true,
23074        /**
23075         * @event move
23076         * Fires when this node is moved to a new location in the tree
23077         * @param {Tree} tree The owner tree
23078         * @param {Node} this This node
23079         * @param {Node} oldParent The old parent of this node
23080         * @param {Node} newParent The new parent of this node
23081         * @param {Number} index The index it was moved to
23082         */
23083        "move" : true,
23084        /**
23085         * @event insert
23086         * Fires when a new child node is inserted.
23087         * @param {Tree} tree The owner tree
23088         * @param {Node} this This node
23089         * @param {Node} node The child node inserted
23090         * @param {Node} refNode The child node the node was inserted before
23091         */
23092        "insert" : true,
23093        /**
23094         * @event beforeappend
23095         * Fires before a new child is appended, return false to cancel the append.
23096         * @param {Tree} tree The owner tree
23097         * @param {Node} this This node
23098         * @param {Node} node The child node to be appended
23099         */
23100        "beforeappend" : true,
23101        /**
23102         * @event beforeremove
23103         * Fires before a child is removed, return false to cancel the remove.
23104         * @param {Tree} tree The owner tree
23105         * @param {Node} this This node
23106         * @param {Node} node The child node to be removed
23107         */
23108        "beforeremove" : true,
23109        /**
23110         * @event beforemove
23111         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23112         * @param {Tree} tree The owner tree
23113         * @param {Node} this This node
23114         * @param {Node} oldParent The parent of this node
23115         * @param {Node} newParent The new parent this node is moving to
23116         * @param {Number} index The index it is being moved to
23117         */
23118        "beforemove" : true,
23119        /**
23120         * @event beforeinsert
23121         * Fires before a new child is inserted, return false to cancel the insert.
23122         * @param {Tree} tree The owner tree
23123         * @param {Node} this This node
23124         * @param {Node} node The child node to be inserted
23125         * @param {Node} refNode The child node the node is being inserted before
23126         */
23127        "beforeinsert" : true
23128    });
23129     this.listeners = this.attributes.listeners;
23130     Roo.data.Node.superclass.constructor.call(this);
23131 };
23132
23133 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23134     fireEvent : function(evtName){
23135         // first do standard event for this node
23136         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23137             return false;
23138         }
23139         // then bubble it up to the tree if the event wasn't cancelled
23140         var ot = this.getOwnerTree();
23141         if(ot){
23142             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23143                 return false;
23144             }
23145         }
23146         return true;
23147     },
23148
23149     /**
23150      * Returns true if this node is a leaf
23151      * @return {Boolean}
23152      */
23153     isLeaf : function(){
23154         return this.leaf === true;
23155     },
23156
23157     // private
23158     setFirstChild : function(node){
23159         this.firstChild = node;
23160     },
23161
23162     //private
23163     setLastChild : function(node){
23164         this.lastChild = node;
23165     },
23166
23167
23168     /**
23169      * Returns true if this node is the last child of its parent
23170      * @return {Boolean}
23171      */
23172     isLast : function(){
23173        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23174     },
23175
23176     /**
23177      * Returns true if this node is the first child of its parent
23178      * @return {Boolean}
23179      */
23180     isFirst : function(){
23181        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23182     },
23183
23184     hasChildNodes : function(){
23185         return !this.isLeaf() && this.childNodes.length > 0;
23186     },
23187
23188     /**
23189      * Insert node(s) as the last child node of this node.
23190      * @param {Node/Array} node The node or Array of nodes to append
23191      * @return {Node} The appended node if single append, or null if an array was passed
23192      */
23193     appendChild : function(node){
23194         var multi = false;
23195         if(node instanceof Array){
23196             multi = node;
23197         }else if(arguments.length > 1){
23198             multi = arguments;
23199         }
23200         // if passed an array or multiple args do them one by one
23201         if(multi){
23202             for(var i = 0, len = multi.length; i < len; i++) {
23203                 this.appendChild(multi[i]);
23204             }
23205         }else{
23206             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23207                 return false;
23208             }
23209             var index = this.childNodes.length;
23210             var oldParent = node.parentNode;
23211             // it's a move, make sure we move it cleanly
23212             if(oldParent){
23213                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23214                     return false;
23215                 }
23216                 oldParent.removeChild(node);
23217             }
23218             index = this.childNodes.length;
23219             if(index == 0){
23220                 this.setFirstChild(node);
23221             }
23222             this.childNodes.push(node);
23223             node.parentNode = this;
23224             var ps = this.childNodes[index-1];
23225             if(ps){
23226                 node.previousSibling = ps;
23227                 ps.nextSibling = node;
23228             }else{
23229                 node.previousSibling = null;
23230             }
23231             node.nextSibling = null;
23232             this.setLastChild(node);
23233             node.setOwnerTree(this.getOwnerTree());
23234             this.fireEvent("append", this.ownerTree, this, node, index);
23235             if(oldParent){
23236                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23237             }
23238             return node;
23239         }
23240     },
23241
23242     /**
23243      * Removes a child node from this node.
23244      * @param {Node} node The node to remove
23245      * @return {Node} The removed node
23246      */
23247     removeChild : function(node){
23248         var index = this.childNodes.indexOf(node);
23249         if(index == -1){
23250             return false;
23251         }
23252         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23253             return false;
23254         }
23255
23256         // remove it from childNodes collection
23257         this.childNodes.splice(index, 1);
23258
23259         // update siblings
23260         if(node.previousSibling){
23261             node.previousSibling.nextSibling = node.nextSibling;
23262         }
23263         if(node.nextSibling){
23264             node.nextSibling.previousSibling = node.previousSibling;
23265         }
23266
23267         // update child refs
23268         if(this.firstChild == node){
23269             this.setFirstChild(node.nextSibling);
23270         }
23271         if(this.lastChild == node){
23272             this.setLastChild(node.previousSibling);
23273         }
23274
23275         node.setOwnerTree(null);
23276         // clear any references from the node
23277         node.parentNode = null;
23278         node.previousSibling = null;
23279         node.nextSibling = null;
23280         this.fireEvent("remove", this.ownerTree, this, node);
23281         return node;
23282     },
23283
23284     /**
23285      * Inserts the first node before the second node in this nodes childNodes collection.
23286      * @param {Node} node The node to insert
23287      * @param {Node} refNode The node to insert before (if null the node is appended)
23288      * @return {Node} The inserted node
23289      */
23290     insertBefore : function(node, refNode){
23291         if(!refNode){ // like standard Dom, refNode can be null for append
23292             return this.appendChild(node);
23293         }
23294         // nothing to do
23295         if(node == refNode){
23296             return false;
23297         }
23298
23299         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23300             return false;
23301         }
23302         var index = this.childNodes.indexOf(refNode);
23303         var oldParent = node.parentNode;
23304         var refIndex = index;
23305
23306         // when moving internally, indexes will change after remove
23307         if(oldParent == this && this.childNodes.indexOf(node) < index){
23308             refIndex--;
23309         }
23310
23311         // it's a move, make sure we move it cleanly
23312         if(oldParent){
23313             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23314                 return false;
23315             }
23316             oldParent.removeChild(node);
23317         }
23318         if(refIndex == 0){
23319             this.setFirstChild(node);
23320         }
23321         this.childNodes.splice(refIndex, 0, node);
23322         node.parentNode = this;
23323         var ps = this.childNodes[refIndex-1];
23324         if(ps){
23325             node.previousSibling = ps;
23326             ps.nextSibling = node;
23327         }else{
23328             node.previousSibling = null;
23329         }
23330         node.nextSibling = refNode;
23331         refNode.previousSibling = node;
23332         node.setOwnerTree(this.getOwnerTree());
23333         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23334         if(oldParent){
23335             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23336         }
23337         return node;
23338     },
23339
23340     /**
23341      * Returns the child node at the specified index.
23342      * @param {Number} index
23343      * @return {Node}
23344      */
23345     item : function(index){
23346         return this.childNodes[index];
23347     },
23348
23349     /**
23350      * Replaces one child node in this node with another.
23351      * @param {Node} newChild The replacement node
23352      * @param {Node} oldChild The node to replace
23353      * @return {Node} The replaced node
23354      */
23355     replaceChild : function(newChild, oldChild){
23356         this.insertBefore(newChild, oldChild);
23357         this.removeChild(oldChild);
23358         return oldChild;
23359     },
23360
23361     /**
23362      * Returns the index of a child node
23363      * @param {Node} node
23364      * @return {Number} The index of the node or -1 if it was not found
23365      */
23366     indexOf : function(child){
23367         return this.childNodes.indexOf(child);
23368     },
23369
23370     /**
23371      * Returns the tree this node is in.
23372      * @return {Tree}
23373      */
23374     getOwnerTree : function(){
23375         // if it doesn't have one, look for one
23376         if(!this.ownerTree){
23377             var p = this;
23378             while(p){
23379                 if(p.ownerTree){
23380                     this.ownerTree = p.ownerTree;
23381                     break;
23382                 }
23383                 p = p.parentNode;
23384             }
23385         }
23386         return this.ownerTree;
23387     },
23388
23389     /**
23390      * Returns depth of this node (the root node has a depth of 0)
23391      * @return {Number}
23392      */
23393     getDepth : function(){
23394         var depth = 0;
23395         var p = this;
23396         while(p.parentNode){
23397             ++depth;
23398             p = p.parentNode;
23399         }
23400         return depth;
23401     },
23402
23403     // private
23404     setOwnerTree : function(tree){
23405         // if it's move, we need to update everyone
23406         if(tree != this.ownerTree){
23407             if(this.ownerTree){
23408                 this.ownerTree.unregisterNode(this);
23409             }
23410             this.ownerTree = tree;
23411             var cs = this.childNodes;
23412             for(var i = 0, len = cs.length; i < len; i++) {
23413                 cs[i].setOwnerTree(tree);
23414             }
23415             if(tree){
23416                 tree.registerNode(this);
23417             }
23418         }
23419     },
23420
23421     /**
23422      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23423      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23424      * @return {String} The path
23425      */
23426     getPath : function(attr){
23427         attr = attr || "id";
23428         var p = this.parentNode;
23429         var b = [this.attributes[attr]];
23430         while(p){
23431             b.unshift(p.attributes[attr]);
23432             p = p.parentNode;
23433         }
23434         var sep = this.getOwnerTree().pathSeparator;
23435         return sep + b.join(sep);
23436     },
23437
23438     /**
23439      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23440      * function call will be the scope provided or the current node. The arguments to the function
23441      * will be the args provided or the current node. If the function returns false at any point,
23442      * the bubble is stopped.
23443      * @param {Function} fn The function to call
23444      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23445      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23446      */
23447     bubble : function(fn, scope, args){
23448         var p = this;
23449         while(p){
23450             if(fn.call(scope || p, args || p) === false){
23451                 break;
23452             }
23453             p = p.parentNode;
23454         }
23455     },
23456
23457     /**
23458      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23459      * function call will be the scope provided or the current node. The arguments to the function
23460      * will be the args provided or the current node. If the function returns false at any point,
23461      * the cascade is stopped on that branch.
23462      * @param {Function} fn The function to call
23463      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23464      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23465      */
23466     cascade : function(fn, scope, args){
23467         if(fn.call(scope || this, args || this) !== false){
23468             var cs = this.childNodes;
23469             for(var i = 0, len = cs.length; i < len; i++) {
23470                 cs[i].cascade(fn, scope, args);
23471             }
23472         }
23473     },
23474
23475     /**
23476      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23477      * function call will be the scope provided or the current node. The arguments to the function
23478      * will be the args provided or the current node. If the function returns false at any point,
23479      * the iteration stops.
23480      * @param {Function} fn The function to call
23481      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23482      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23483      */
23484     eachChild : function(fn, scope, args){
23485         var cs = this.childNodes;
23486         for(var i = 0, len = cs.length; i < len; i++) {
23487                 if(fn.call(scope || this, args || cs[i]) === false){
23488                     break;
23489                 }
23490         }
23491     },
23492
23493     /**
23494      * Finds the first child that has the attribute with the specified value.
23495      * @param {String} attribute The attribute name
23496      * @param {Mixed} value The value to search for
23497      * @return {Node} The found child or null if none was found
23498      */
23499     findChild : function(attribute, value){
23500         var cs = this.childNodes;
23501         for(var i = 0, len = cs.length; i < len; i++) {
23502                 if(cs[i].attributes[attribute] == value){
23503                     return cs[i];
23504                 }
23505         }
23506         return null;
23507     },
23508
23509     /**
23510      * Finds the first child by a custom function. The child matches if the function passed
23511      * returns true.
23512      * @param {Function} fn
23513      * @param {Object} scope (optional)
23514      * @return {Node} The found child or null if none was found
23515      */
23516     findChildBy : function(fn, scope){
23517         var cs = this.childNodes;
23518         for(var i = 0, len = cs.length; i < len; i++) {
23519                 if(fn.call(scope||cs[i], cs[i]) === true){
23520                     return cs[i];
23521                 }
23522         }
23523         return null;
23524     },
23525
23526     /**
23527      * Sorts this nodes children using the supplied sort function
23528      * @param {Function} fn
23529      * @param {Object} scope (optional)
23530      */
23531     sort : function(fn, scope){
23532         var cs = this.childNodes;
23533         var len = cs.length;
23534         if(len > 0){
23535             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23536             cs.sort(sortFn);
23537             for(var i = 0; i < len; i++){
23538                 var n = cs[i];
23539                 n.previousSibling = cs[i-1];
23540                 n.nextSibling = cs[i+1];
23541                 if(i == 0){
23542                     this.setFirstChild(n);
23543                 }
23544                 if(i == len-1){
23545                     this.setLastChild(n);
23546                 }
23547             }
23548         }
23549     },
23550
23551     /**
23552      * Returns true if this node is an ancestor (at any point) of the passed node.
23553      * @param {Node} node
23554      * @return {Boolean}
23555      */
23556     contains : function(node){
23557         return node.isAncestor(this);
23558     },
23559
23560     /**
23561      * Returns true if the passed node is an ancestor (at any point) of this node.
23562      * @param {Node} node
23563      * @return {Boolean}
23564      */
23565     isAncestor : function(node){
23566         var p = this.parentNode;
23567         while(p){
23568             if(p == node){
23569                 return true;
23570             }
23571             p = p.parentNode;
23572         }
23573         return false;
23574     },
23575
23576     toString : function(){
23577         return "[Node"+(this.id?" "+this.id:"")+"]";
23578     }
23579 });/*
23580  * Based on:
23581  * Ext JS Library 1.1.1
23582  * Copyright(c) 2006-2007, Ext JS, LLC.
23583  *
23584  * Originally Released Under LGPL - original licence link has changed is not relivant.
23585  *
23586  * Fork - LGPL
23587  * <script type="text/javascript">
23588  */
23589  (function(){ 
23590 /**
23591  * @class Roo.Layer
23592  * @extends Roo.Element
23593  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23594  * automatic maintaining of shadow/shim positions.
23595  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23596  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23597  * you can pass a string with a CSS class name. False turns off the shadow.
23598  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23599  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23600  * @cfg {String} cls CSS class to add to the element
23601  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23602  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23603  * @constructor
23604  * @param {Object} config An object with config options.
23605  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23606  */
23607
23608 Roo.Layer = function(config, existingEl){
23609     config = config || {};
23610     var dh = Roo.DomHelper;
23611     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23612     if(existingEl){
23613         this.dom = Roo.getDom(existingEl);
23614     }
23615     if(!this.dom){
23616         var o = config.dh || {tag: "div", cls: "x-layer"};
23617         this.dom = dh.append(pel, o);
23618     }
23619     if(config.cls){
23620         this.addClass(config.cls);
23621     }
23622     this.constrain = config.constrain !== false;
23623     this.visibilityMode = Roo.Element.VISIBILITY;
23624     if(config.id){
23625         this.id = this.dom.id = config.id;
23626     }else{
23627         this.id = Roo.id(this.dom);
23628     }
23629     this.zindex = config.zindex || this.getZIndex();
23630     this.position("absolute", this.zindex);
23631     if(config.shadow){
23632         this.shadowOffset = config.shadowOffset || 4;
23633         this.shadow = new Roo.Shadow({
23634             offset : this.shadowOffset,
23635             mode : config.shadow
23636         });
23637     }else{
23638         this.shadowOffset = 0;
23639     }
23640     this.useShim = config.shim !== false && Roo.useShims;
23641     this.useDisplay = config.useDisplay;
23642     this.hide();
23643 };
23644
23645 var supr = Roo.Element.prototype;
23646
23647 // shims are shared among layer to keep from having 100 iframes
23648 var shims = [];
23649
23650 Roo.extend(Roo.Layer, Roo.Element, {
23651
23652     getZIndex : function(){
23653         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23654     },
23655
23656     getShim : function(){
23657         if(!this.useShim){
23658             return null;
23659         }
23660         if(this.shim){
23661             return this.shim;
23662         }
23663         var shim = shims.shift();
23664         if(!shim){
23665             shim = this.createShim();
23666             shim.enableDisplayMode('block');
23667             shim.dom.style.display = 'none';
23668             shim.dom.style.visibility = 'visible';
23669         }
23670         var pn = this.dom.parentNode;
23671         if(shim.dom.parentNode != pn){
23672             pn.insertBefore(shim.dom, this.dom);
23673         }
23674         shim.setStyle('z-index', this.getZIndex()-2);
23675         this.shim = shim;
23676         return shim;
23677     },
23678
23679     hideShim : function(){
23680         if(this.shim){
23681             this.shim.setDisplayed(false);
23682             shims.push(this.shim);
23683             delete this.shim;
23684         }
23685     },
23686
23687     disableShadow : function(){
23688         if(this.shadow){
23689             this.shadowDisabled = true;
23690             this.shadow.hide();
23691             this.lastShadowOffset = this.shadowOffset;
23692             this.shadowOffset = 0;
23693         }
23694     },
23695
23696     enableShadow : function(show){
23697         if(this.shadow){
23698             this.shadowDisabled = false;
23699             this.shadowOffset = this.lastShadowOffset;
23700             delete this.lastShadowOffset;
23701             if(show){
23702                 this.sync(true);
23703             }
23704         }
23705     },
23706
23707     // private
23708     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23709     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23710     sync : function(doShow){
23711         var sw = this.shadow;
23712         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23713             var sh = this.getShim();
23714
23715             var w = this.getWidth(),
23716                 h = this.getHeight();
23717
23718             var l = this.getLeft(true),
23719                 t = this.getTop(true);
23720
23721             if(sw && !this.shadowDisabled){
23722                 if(doShow && !sw.isVisible()){
23723                     sw.show(this);
23724                 }else{
23725                     sw.realign(l, t, w, h);
23726                 }
23727                 if(sh){
23728                     if(doShow){
23729                        sh.show();
23730                     }
23731                     // fit the shim behind the shadow, so it is shimmed too
23732                     var a = sw.adjusts, s = sh.dom.style;
23733                     s.left = (Math.min(l, l+a.l))+"px";
23734                     s.top = (Math.min(t, t+a.t))+"px";
23735                     s.width = (w+a.w)+"px";
23736                     s.height = (h+a.h)+"px";
23737                 }
23738             }else if(sh){
23739                 if(doShow){
23740                    sh.show();
23741                 }
23742                 sh.setSize(w, h);
23743                 sh.setLeftTop(l, t);
23744             }
23745             
23746         }
23747     },
23748
23749     // private
23750     destroy : function(){
23751         this.hideShim();
23752         if(this.shadow){
23753             this.shadow.hide();
23754         }
23755         this.removeAllListeners();
23756         var pn = this.dom.parentNode;
23757         if(pn){
23758             pn.removeChild(this.dom);
23759         }
23760         Roo.Element.uncache(this.id);
23761     },
23762
23763     remove : function(){
23764         this.destroy();
23765     },
23766
23767     // private
23768     beginUpdate : function(){
23769         this.updating = true;
23770     },
23771
23772     // private
23773     endUpdate : function(){
23774         this.updating = false;
23775         this.sync(true);
23776     },
23777
23778     // private
23779     hideUnders : function(negOffset){
23780         if(this.shadow){
23781             this.shadow.hide();
23782         }
23783         this.hideShim();
23784     },
23785
23786     // private
23787     constrainXY : function(){
23788         if(this.constrain){
23789             var vw = Roo.lib.Dom.getViewWidth(),
23790                 vh = Roo.lib.Dom.getViewHeight();
23791             var s = Roo.get(document).getScroll();
23792
23793             var xy = this.getXY();
23794             var x = xy[0], y = xy[1];   
23795             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23796             // only move it if it needs it
23797             var moved = false;
23798             // first validate right/bottom
23799             if((x + w) > vw+s.left){
23800                 x = vw - w - this.shadowOffset;
23801                 moved = true;
23802             }
23803             if((y + h) > vh+s.top){
23804                 y = vh - h - this.shadowOffset;
23805                 moved = true;
23806             }
23807             // then make sure top/left isn't negative
23808             if(x < s.left){
23809                 x = s.left;
23810                 moved = true;
23811             }
23812             if(y < s.top){
23813                 y = s.top;
23814                 moved = true;
23815             }
23816             if(moved){
23817                 if(this.avoidY){
23818                     var ay = this.avoidY;
23819                     if(y <= ay && (y+h) >= ay){
23820                         y = ay-h-5;   
23821                     }
23822                 }
23823                 xy = [x, y];
23824                 this.storeXY(xy);
23825                 supr.setXY.call(this, xy);
23826                 this.sync();
23827             }
23828         }
23829     },
23830
23831     isVisible : function(){
23832         return this.visible;    
23833     },
23834
23835     // private
23836     showAction : function(){
23837         this.visible = true; // track visibility to prevent getStyle calls
23838         if(this.useDisplay === true){
23839             this.setDisplayed("");
23840         }else if(this.lastXY){
23841             supr.setXY.call(this, this.lastXY);
23842         }else if(this.lastLT){
23843             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23844         }
23845     },
23846
23847     // private
23848     hideAction : function(){
23849         this.visible = false;
23850         if(this.useDisplay === true){
23851             this.setDisplayed(false);
23852         }else{
23853             this.setLeftTop(-10000,-10000);
23854         }
23855     },
23856
23857     // overridden Element method
23858     setVisible : function(v, a, d, c, e){
23859         if(v){
23860             this.showAction();
23861         }
23862         if(a && v){
23863             var cb = function(){
23864                 this.sync(true);
23865                 if(c){
23866                     c();
23867                 }
23868             }.createDelegate(this);
23869             supr.setVisible.call(this, true, true, d, cb, e);
23870         }else{
23871             if(!v){
23872                 this.hideUnders(true);
23873             }
23874             var cb = c;
23875             if(a){
23876                 cb = function(){
23877                     this.hideAction();
23878                     if(c){
23879                         c();
23880                     }
23881                 }.createDelegate(this);
23882             }
23883             supr.setVisible.call(this, v, a, d, cb, e);
23884             if(v){
23885                 this.sync(true);
23886             }else if(!a){
23887                 this.hideAction();
23888             }
23889         }
23890     },
23891
23892     storeXY : function(xy){
23893         delete this.lastLT;
23894         this.lastXY = xy;
23895     },
23896
23897     storeLeftTop : function(left, top){
23898         delete this.lastXY;
23899         this.lastLT = [left, top];
23900     },
23901
23902     // private
23903     beforeFx : function(){
23904         this.beforeAction();
23905         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23906     },
23907
23908     // private
23909     afterFx : function(){
23910         Roo.Layer.superclass.afterFx.apply(this, arguments);
23911         this.sync(this.isVisible());
23912     },
23913
23914     // private
23915     beforeAction : function(){
23916         if(!this.updating && this.shadow){
23917             this.shadow.hide();
23918         }
23919     },
23920
23921     // overridden Element method
23922     setLeft : function(left){
23923         this.storeLeftTop(left, this.getTop(true));
23924         supr.setLeft.apply(this, arguments);
23925         this.sync();
23926     },
23927
23928     setTop : function(top){
23929         this.storeLeftTop(this.getLeft(true), top);
23930         supr.setTop.apply(this, arguments);
23931         this.sync();
23932     },
23933
23934     setLeftTop : function(left, top){
23935         this.storeLeftTop(left, top);
23936         supr.setLeftTop.apply(this, arguments);
23937         this.sync();
23938     },
23939
23940     setXY : function(xy, a, d, c, e){
23941         this.fixDisplay();
23942         this.beforeAction();
23943         this.storeXY(xy);
23944         var cb = this.createCB(c);
23945         supr.setXY.call(this, xy, a, d, cb, e);
23946         if(!a){
23947             cb();
23948         }
23949     },
23950
23951     // private
23952     createCB : function(c){
23953         var el = this;
23954         return function(){
23955             el.constrainXY();
23956             el.sync(true);
23957             if(c){
23958                 c();
23959             }
23960         };
23961     },
23962
23963     // overridden Element method
23964     setX : function(x, a, d, c, e){
23965         this.setXY([x, this.getY()], a, d, c, e);
23966     },
23967
23968     // overridden Element method
23969     setY : function(y, a, d, c, e){
23970         this.setXY([this.getX(), y], a, d, c, e);
23971     },
23972
23973     // overridden Element method
23974     setSize : function(w, h, a, d, c, e){
23975         this.beforeAction();
23976         var cb = this.createCB(c);
23977         supr.setSize.call(this, w, h, a, d, cb, e);
23978         if(!a){
23979             cb();
23980         }
23981     },
23982
23983     // overridden Element method
23984     setWidth : function(w, a, d, c, e){
23985         this.beforeAction();
23986         var cb = this.createCB(c);
23987         supr.setWidth.call(this, w, a, d, cb, e);
23988         if(!a){
23989             cb();
23990         }
23991     },
23992
23993     // overridden Element method
23994     setHeight : function(h, a, d, c, e){
23995         this.beforeAction();
23996         var cb = this.createCB(c);
23997         supr.setHeight.call(this, h, a, d, cb, e);
23998         if(!a){
23999             cb();
24000         }
24001     },
24002
24003     // overridden Element method
24004     setBounds : function(x, y, w, h, a, d, c, e){
24005         this.beforeAction();
24006         var cb = this.createCB(c);
24007         if(!a){
24008             this.storeXY([x, y]);
24009             supr.setXY.call(this, [x, y]);
24010             supr.setSize.call(this, w, h, a, d, cb, e);
24011             cb();
24012         }else{
24013             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24014         }
24015         return this;
24016     },
24017     
24018     /**
24019      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24020      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24021      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24022      * @param {Number} zindex The new z-index to set
24023      * @return {this} The Layer
24024      */
24025     setZIndex : function(zindex){
24026         this.zindex = zindex;
24027         this.setStyle("z-index", zindex + 2);
24028         if(this.shadow){
24029             this.shadow.setZIndex(zindex + 1);
24030         }
24031         if(this.shim){
24032             this.shim.setStyle("z-index", zindex);
24033         }
24034     }
24035 });
24036 })();/*
24037  * Based on:
24038  * Ext JS Library 1.1.1
24039  * Copyright(c) 2006-2007, Ext JS, LLC.
24040  *
24041  * Originally Released Under LGPL - original licence link has changed is not relivant.
24042  *
24043  * Fork - LGPL
24044  * <script type="text/javascript">
24045  */
24046
24047
24048 /**
24049  * @class Roo.Shadow
24050  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24051  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24052  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24053  * @constructor
24054  * Create a new Shadow
24055  * @param {Object} config The config object
24056  */
24057 Roo.Shadow = function(config){
24058     Roo.apply(this, config);
24059     if(typeof this.mode != "string"){
24060         this.mode = this.defaultMode;
24061     }
24062     var o = this.offset, a = {h: 0};
24063     var rad = Math.floor(this.offset/2);
24064     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24065         case "drop":
24066             a.w = 0;
24067             a.l = a.t = o;
24068             a.t -= 1;
24069             if(Roo.isIE){
24070                 a.l -= this.offset + rad;
24071                 a.t -= this.offset + rad;
24072                 a.w -= rad;
24073                 a.h -= rad;
24074                 a.t += 1;
24075             }
24076         break;
24077         case "sides":
24078             a.w = (o*2);
24079             a.l = -o;
24080             a.t = o-1;
24081             if(Roo.isIE){
24082                 a.l -= (this.offset - rad);
24083                 a.t -= this.offset + rad;
24084                 a.l += 1;
24085                 a.w -= (this.offset - rad)*2;
24086                 a.w -= rad + 1;
24087                 a.h -= 1;
24088             }
24089         break;
24090         case "frame":
24091             a.w = a.h = (o*2);
24092             a.l = a.t = -o;
24093             a.t += 1;
24094             a.h -= 2;
24095             if(Roo.isIE){
24096                 a.l -= (this.offset - rad);
24097                 a.t -= (this.offset - rad);
24098                 a.l += 1;
24099                 a.w -= (this.offset + rad + 1);
24100                 a.h -= (this.offset + rad);
24101                 a.h += 1;
24102             }
24103         break;
24104     };
24105
24106     this.adjusts = a;
24107 };
24108
24109 Roo.Shadow.prototype = {
24110     /**
24111      * @cfg {String} mode
24112      * The shadow display mode.  Supports the following options:<br />
24113      * sides: Shadow displays on both sides and bottom only<br />
24114      * frame: Shadow displays equally on all four sides<br />
24115      * drop: Traditional bottom-right drop shadow (default)
24116      */
24117     /**
24118      * @cfg {String} offset
24119      * The number of pixels to offset the shadow from the element (defaults to 4)
24120      */
24121     offset: 4,
24122
24123     // private
24124     defaultMode: "drop",
24125
24126     /**
24127      * Displays the shadow under the target element
24128      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24129      */
24130     show : function(target){
24131         target = Roo.get(target);
24132         if(!this.el){
24133             this.el = Roo.Shadow.Pool.pull();
24134             if(this.el.dom.nextSibling != target.dom){
24135                 this.el.insertBefore(target);
24136             }
24137         }
24138         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24139         if(Roo.isIE){
24140             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24141         }
24142         this.realign(
24143             target.getLeft(true),
24144             target.getTop(true),
24145             target.getWidth(),
24146             target.getHeight()
24147         );
24148         this.el.dom.style.display = "block";
24149     },
24150
24151     /**
24152      * Returns true if the shadow is visible, else false
24153      */
24154     isVisible : function(){
24155         return this.el ? true : false;  
24156     },
24157
24158     /**
24159      * Direct alignment when values are already available. Show must be called at least once before
24160      * calling this method to ensure it is initialized.
24161      * @param {Number} left The target element left position
24162      * @param {Number} top The target element top position
24163      * @param {Number} width The target element width
24164      * @param {Number} height The target element height
24165      */
24166     realign : function(l, t, w, h){
24167         if(!this.el){
24168             return;
24169         }
24170         var a = this.adjusts, d = this.el.dom, s = d.style;
24171         var iea = 0;
24172         s.left = (l+a.l)+"px";
24173         s.top = (t+a.t)+"px";
24174         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24175  
24176         if(s.width != sws || s.height != shs){
24177             s.width = sws;
24178             s.height = shs;
24179             if(!Roo.isIE){
24180                 var cn = d.childNodes;
24181                 var sww = Math.max(0, (sw-12))+"px";
24182                 cn[0].childNodes[1].style.width = sww;
24183                 cn[1].childNodes[1].style.width = sww;
24184                 cn[2].childNodes[1].style.width = sww;
24185                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24186             }
24187         }
24188     },
24189
24190     /**
24191      * Hides this shadow
24192      */
24193     hide : function(){
24194         if(this.el){
24195             this.el.dom.style.display = "none";
24196             Roo.Shadow.Pool.push(this.el);
24197             delete this.el;
24198         }
24199     },
24200
24201     /**
24202      * Adjust the z-index of this shadow
24203      * @param {Number} zindex The new z-index
24204      */
24205     setZIndex : function(z){
24206         this.zIndex = z;
24207         if(this.el){
24208             this.el.setStyle("z-index", z);
24209         }
24210     }
24211 };
24212
24213 // Private utility class that manages the internal Shadow cache
24214 Roo.Shadow.Pool = function(){
24215     var p = [];
24216     var markup = Roo.isIE ?
24217                  '<div class="x-ie-shadow"></div>' :
24218                  '<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>';
24219     return {
24220         pull : function(){
24221             var sh = p.shift();
24222             if(!sh){
24223                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24224                 sh.autoBoxAdjust = false;
24225             }
24226             return sh;
24227         },
24228
24229         push : function(sh){
24230             p.push(sh);
24231         }
24232     };
24233 }();/*
24234  * Based on:
24235  * Ext JS Library 1.1.1
24236  * Copyright(c) 2006-2007, Ext JS, LLC.
24237  *
24238  * Originally Released Under LGPL - original licence link has changed is not relivant.
24239  *
24240  * Fork - LGPL
24241  * <script type="text/javascript">
24242  */
24243
24244
24245 /**
24246  * @class Roo.SplitBar
24247  * @extends Roo.util.Observable
24248  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24249  * <br><br>
24250  * Usage:
24251  * <pre><code>
24252 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24253                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24254 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24255 split.minSize = 100;
24256 split.maxSize = 600;
24257 split.animate = true;
24258 split.on('moved', splitterMoved);
24259 </code></pre>
24260  * @constructor
24261  * Create a new SplitBar
24262  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24263  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24264  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24265  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24266                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24267                         position of the SplitBar).
24268  */
24269 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24270     
24271     /** @private */
24272     this.el = Roo.get(dragElement, true);
24273     this.el.dom.unselectable = "on";
24274     /** @private */
24275     this.resizingEl = Roo.get(resizingElement, true);
24276
24277     /**
24278      * @private
24279      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24280      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24281      * @type Number
24282      */
24283     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24284     
24285     /**
24286      * The minimum size of the resizing element. (Defaults to 0)
24287      * @type Number
24288      */
24289     this.minSize = 0;
24290     
24291     /**
24292      * The maximum size of the resizing element. (Defaults to 2000)
24293      * @type Number
24294      */
24295     this.maxSize = 2000;
24296     
24297     /**
24298      * Whether to animate the transition to the new size
24299      * @type Boolean
24300      */
24301     this.animate = false;
24302     
24303     /**
24304      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24305      * @type Boolean
24306      */
24307     this.useShim = false;
24308     
24309     /** @private */
24310     this.shim = null;
24311     
24312     if(!existingProxy){
24313         /** @private */
24314         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24315     }else{
24316         this.proxy = Roo.get(existingProxy).dom;
24317     }
24318     /** @private */
24319     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24320     
24321     /** @private */
24322     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24323     
24324     /** @private */
24325     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24326     
24327     /** @private */
24328     this.dragSpecs = {};
24329     
24330     /**
24331      * @private The adapter to use to positon and resize elements
24332      */
24333     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24334     this.adapter.init(this);
24335     
24336     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24337         /** @private */
24338         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24339         this.el.addClass("x-splitbar-h");
24340     }else{
24341         /** @private */
24342         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24343         this.el.addClass("x-splitbar-v");
24344     }
24345     
24346     this.addEvents({
24347         /**
24348          * @event resize
24349          * Fires when the splitter is moved (alias for {@link #event-moved})
24350          * @param {Roo.SplitBar} this
24351          * @param {Number} newSize the new width or height
24352          */
24353         "resize" : true,
24354         /**
24355          * @event moved
24356          * Fires when the splitter is moved
24357          * @param {Roo.SplitBar} this
24358          * @param {Number} newSize the new width or height
24359          */
24360         "moved" : true,
24361         /**
24362          * @event beforeresize
24363          * Fires before the splitter is dragged
24364          * @param {Roo.SplitBar} this
24365          */
24366         "beforeresize" : true,
24367
24368         "beforeapply" : true
24369     });
24370
24371     Roo.util.Observable.call(this);
24372 };
24373
24374 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24375     onStartProxyDrag : function(x, y){
24376         this.fireEvent("beforeresize", this);
24377         if(!this.overlay){
24378             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24379             o.unselectable();
24380             o.enableDisplayMode("block");
24381             // all splitbars share the same overlay
24382             Roo.SplitBar.prototype.overlay = o;
24383         }
24384         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24385         this.overlay.show();
24386         Roo.get(this.proxy).setDisplayed("block");
24387         var size = this.adapter.getElementSize(this);
24388         this.activeMinSize = this.getMinimumSize();;
24389         this.activeMaxSize = this.getMaximumSize();;
24390         var c1 = size - this.activeMinSize;
24391         var c2 = Math.max(this.activeMaxSize - size, 0);
24392         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24393             this.dd.resetConstraints();
24394             this.dd.setXConstraint(
24395                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24396                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24397             );
24398             this.dd.setYConstraint(0, 0);
24399         }else{
24400             this.dd.resetConstraints();
24401             this.dd.setXConstraint(0, 0);
24402             this.dd.setYConstraint(
24403                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24404                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24405             );
24406          }
24407         this.dragSpecs.startSize = size;
24408         this.dragSpecs.startPoint = [x, y];
24409         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24410     },
24411     
24412     /** 
24413      * @private Called after the drag operation by the DDProxy
24414      */
24415     onEndProxyDrag : function(e){
24416         Roo.get(this.proxy).setDisplayed(false);
24417         var endPoint = Roo.lib.Event.getXY(e);
24418         if(this.overlay){
24419             this.overlay.hide();
24420         }
24421         var newSize;
24422         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24423             newSize = this.dragSpecs.startSize + 
24424                 (this.placement == Roo.SplitBar.LEFT ?
24425                     endPoint[0] - this.dragSpecs.startPoint[0] :
24426                     this.dragSpecs.startPoint[0] - endPoint[0]
24427                 );
24428         }else{
24429             newSize = this.dragSpecs.startSize + 
24430                 (this.placement == Roo.SplitBar.TOP ?
24431                     endPoint[1] - this.dragSpecs.startPoint[1] :
24432                     this.dragSpecs.startPoint[1] - endPoint[1]
24433                 );
24434         }
24435         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24436         if(newSize != this.dragSpecs.startSize){
24437             if(this.fireEvent('beforeapply', this, newSize) !== false){
24438                 this.adapter.setElementSize(this, newSize);
24439                 this.fireEvent("moved", this, newSize);
24440                 this.fireEvent("resize", this, newSize);
24441             }
24442         }
24443     },
24444     
24445     /**
24446      * Get the adapter this SplitBar uses
24447      * @return The adapter object
24448      */
24449     getAdapter : function(){
24450         return this.adapter;
24451     },
24452     
24453     /**
24454      * Set the adapter this SplitBar uses
24455      * @param {Object} adapter A SplitBar adapter object
24456      */
24457     setAdapter : function(adapter){
24458         this.adapter = adapter;
24459         this.adapter.init(this);
24460     },
24461     
24462     /**
24463      * Gets the minimum size for the resizing element
24464      * @return {Number} The minimum size
24465      */
24466     getMinimumSize : function(){
24467         return this.minSize;
24468     },
24469     
24470     /**
24471      * Sets the minimum size for the resizing element
24472      * @param {Number} minSize The minimum size
24473      */
24474     setMinimumSize : function(minSize){
24475         this.minSize = minSize;
24476     },
24477     
24478     /**
24479      * Gets the maximum size for the resizing element
24480      * @return {Number} The maximum size
24481      */
24482     getMaximumSize : function(){
24483         return this.maxSize;
24484     },
24485     
24486     /**
24487      * Sets the maximum size for the resizing element
24488      * @param {Number} maxSize The maximum size
24489      */
24490     setMaximumSize : function(maxSize){
24491         this.maxSize = maxSize;
24492     },
24493     
24494     /**
24495      * Sets the initialize size for the resizing element
24496      * @param {Number} size The initial size
24497      */
24498     setCurrentSize : function(size){
24499         var oldAnimate = this.animate;
24500         this.animate = false;
24501         this.adapter.setElementSize(this, size);
24502         this.animate = oldAnimate;
24503     },
24504     
24505     /**
24506      * Destroy this splitbar. 
24507      * @param {Boolean} removeEl True to remove the element
24508      */
24509     destroy : function(removeEl){
24510         if(this.shim){
24511             this.shim.remove();
24512         }
24513         this.dd.unreg();
24514         this.proxy.parentNode.removeChild(this.proxy);
24515         if(removeEl){
24516             this.el.remove();
24517         }
24518     }
24519 });
24520
24521 /**
24522  * @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.
24523  */
24524 Roo.SplitBar.createProxy = function(dir){
24525     var proxy = new Roo.Element(document.createElement("div"));
24526     proxy.unselectable();
24527     var cls = 'x-splitbar-proxy';
24528     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24529     document.body.appendChild(proxy.dom);
24530     return proxy.dom;
24531 };
24532
24533 /** 
24534  * @class Roo.SplitBar.BasicLayoutAdapter
24535  * Default Adapter. It assumes the splitter and resizing element are not positioned
24536  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24537  */
24538 Roo.SplitBar.BasicLayoutAdapter = function(){
24539 };
24540
24541 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24542     // do nothing for now
24543     init : function(s){
24544     
24545     },
24546     /**
24547      * Called before drag operations to get the current size of the resizing element. 
24548      * @param {Roo.SplitBar} s The SplitBar using this adapter
24549      */
24550      getElementSize : function(s){
24551         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24552             return s.resizingEl.getWidth();
24553         }else{
24554             return s.resizingEl.getHeight();
24555         }
24556     },
24557     
24558     /**
24559      * Called after drag operations to set the size of the resizing element.
24560      * @param {Roo.SplitBar} s The SplitBar using this adapter
24561      * @param {Number} newSize The new size to set
24562      * @param {Function} onComplete A function to be invoked when resizing is complete
24563      */
24564     setElementSize : function(s, newSize, onComplete){
24565         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24566             if(!s.animate){
24567                 s.resizingEl.setWidth(newSize);
24568                 if(onComplete){
24569                     onComplete(s, newSize);
24570                 }
24571             }else{
24572                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24573             }
24574         }else{
24575             
24576             if(!s.animate){
24577                 s.resizingEl.setHeight(newSize);
24578                 if(onComplete){
24579                     onComplete(s, newSize);
24580                 }
24581             }else{
24582                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24583             }
24584         }
24585     }
24586 };
24587
24588 /** 
24589  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24590  * @extends Roo.SplitBar.BasicLayoutAdapter
24591  * Adapter that  moves the splitter element to align with the resized sizing element. 
24592  * Used with an absolute positioned SplitBar.
24593  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24594  * document.body, make sure you assign an id to the body element.
24595  */
24596 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24597     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24598     this.container = Roo.get(container);
24599 };
24600
24601 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24602     init : function(s){
24603         this.basic.init(s);
24604     },
24605     
24606     getElementSize : function(s){
24607         return this.basic.getElementSize(s);
24608     },
24609     
24610     setElementSize : function(s, newSize, onComplete){
24611         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24612     },
24613     
24614     moveSplitter : function(s){
24615         var yes = Roo.SplitBar;
24616         switch(s.placement){
24617             case yes.LEFT:
24618                 s.el.setX(s.resizingEl.getRight());
24619                 break;
24620             case yes.RIGHT:
24621                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24622                 break;
24623             case yes.TOP:
24624                 s.el.setY(s.resizingEl.getBottom());
24625                 break;
24626             case yes.BOTTOM:
24627                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24628                 break;
24629         }
24630     }
24631 };
24632
24633 /**
24634  * Orientation constant - Create a vertical SplitBar
24635  * @static
24636  * @type Number
24637  */
24638 Roo.SplitBar.VERTICAL = 1;
24639
24640 /**
24641  * Orientation constant - Create a horizontal SplitBar
24642  * @static
24643  * @type Number
24644  */
24645 Roo.SplitBar.HORIZONTAL = 2;
24646
24647 /**
24648  * Placement constant - The resizing element is to the left of the splitter element
24649  * @static
24650  * @type Number
24651  */
24652 Roo.SplitBar.LEFT = 1;
24653
24654 /**
24655  * Placement constant - The resizing element is to the right of the splitter element
24656  * @static
24657  * @type Number
24658  */
24659 Roo.SplitBar.RIGHT = 2;
24660
24661 /**
24662  * Placement constant - The resizing element is positioned above the splitter element
24663  * @static
24664  * @type Number
24665  */
24666 Roo.SplitBar.TOP = 3;
24667
24668 /**
24669  * Placement constant - The resizing element is positioned under splitter element
24670  * @static
24671  * @type Number
24672  */
24673 Roo.SplitBar.BOTTOM = 4;
24674 /*
24675  * Based on:
24676  * Ext JS Library 1.1.1
24677  * Copyright(c) 2006-2007, Ext JS, LLC.
24678  *
24679  * Originally Released Under LGPL - original licence link has changed is not relivant.
24680  *
24681  * Fork - LGPL
24682  * <script type="text/javascript">
24683  */
24684
24685 /**
24686  * @class Roo.View
24687  * @extends Roo.util.Observable
24688  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24689  * This class also supports single and multi selection modes. <br>
24690  * Create a data model bound view:
24691  <pre><code>
24692  var store = new Roo.data.Store(...);
24693
24694  var view = new Roo.View({
24695     el : "my-element",
24696     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24697  
24698     singleSelect: true,
24699     selectedClass: "ydataview-selected",
24700     store: store
24701  });
24702
24703  // listen for node click?
24704  view.on("click", function(vw, index, node, e){
24705  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24706  });
24707
24708  // load XML data
24709  dataModel.load("foobar.xml");
24710  </code></pre>
24711  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24712  * <br><br>
24713  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24714  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24715  * 
24716  * Note: old style constructor is still suported (container, template, config)
24717  * 
24718  * @constructor
24719  * Create a new View
24720  * @param {Object} config The config object
24721  * 
24722  */
24723 Roo.View = function(config, depreciated_tpl, depreciated_config){
24724     
24725     if (typeof(depreciated_tpl) == 'undefined') {
24726         // new way.. - universal constructor.
24727         Roo.apply(this, config);
24728         this.el  = Roo.get(this.el);
24729     } else {
24730         // old format..
24731         this.el  = Roo.get(config);
24732         this.tpl = depreciated_tpl;
24733         Roo.apply(this, depreciated_config);
24734     }
24735     this.wrapEl  = this.el.wrap().wrap();
24736     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24737     
24738     
24739     if(typeof(this.tpl) == "string"){
24740         this.tpl = new Roo.Template(this.tpl);
24741     } else {
24742         // support xtype ctors..
24743         this.tpl = new Roo.factory(this.tpl, Roo);
24744     }
24745     
24746     
24747     this.tpl.compile();
24748    
24749   
24750     
24751      
24752     /** @private */
24753     this.addEvents({
24754         /**
24755          * @event beforeclick
24756          * Fires before a click is processed. Returns false to cancel the default action.
24757          * @param {Roo.View} this
24758          * @param {Number} index The index of the target node
24759          * @param {HTMLElement} node The target node
24760          * @param {Roo.EventObject} e The raw event object
24761          */
24762             "beforeclick" : true,
24763         /**
24764          * @event click
24765          * Fires when a template node is clicked.
24766          * @param {Roo.View} this
24767          * @param {Number} index The index of the target node
24768          * @param {HTMLElement} node The target node
24769          * @param {Roo.EventObject} e The raw event object
24770          */
24771             "click" : true,
24772         /**
24773          * @event dblclick
24774          * Fires when a template node is double clicked.
24775          * @param {Roo.View} this
24776          * @param {Number} index The index of the target node
24777          * @param {HTMLElement} node The target node
24778          * @param {Roo.EventObject} e The raw event object
24779          */
24780             "dblclick" : true,
24781         /**
24782          * @event contextmenu
24783          * Fires when a template node is right clicked.
24784          * @param {Roo.View} this
24785          * @param {Number} index The index of the target node
24786          * @param {HTMLElement} node The target node
24787          * @param {Roo.EventObject} e The raw event object
24788          */
24789             "contextmenu" : true,
24790         /**
24791          * @event selectionchange
24792          * Fires when the selected nodes change.
24793          * @param {Roo.View} this
24794          * @param {Array} selections Array of the selected nodes
24795          */
24796             "selectionchange" : true,
24797     
24798         /**
24799          * @event beforeselect
24800          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24801          * @param {Roo.View} this
24802          * @param {HTMLElement} node The node to be selected
24803          * @param {Array} selections Array of currently selected nodes
24804          */
24805             "beforeselect" : true,
24806         /**
24807          * @event preparedata
24808          * Fires on every row to render, to allow you to change the data.
24809          * @param {Roo.View} this
24810          * @param {Object} data to be rendered (change this)
24811          */
24812           "preparedata" : true
24813           
24814           
24815         });
24816
24817
24818
24819     this.el.on({
24820         "click": this.onClick,
24821         "dblclick": this.onDblClick,
24822         "contextmenu": this.onContextMenu,
24823         scope:this
24824     });
24825
24826     this.selections = [];
24827     this.nodes = [];
24828     this.cmp = new Roo.CompositeElementLite([]);
24829     if(this.store){
24830         this.store = Roo.factory(this.store, Roo.data);
24831         this.setStore(this.store, true);
24832     }
24833     
24834     if ( this.footer && this.footer.xtype) {
24835            
24836          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24837         
24838         this.footer.dataSource = this.store
24839         this.footer.container = fctr;
24840         this.footer = Roo.factory(this.footer, Roo);
24841         fctr.insertFirst(this.el);
24842         
24843         // this is a bit insane - as the paging toolbar seems to detach the el..
24844 //        dom.parentNode.parentNode.parentNode
24845          // they get detached?
24846     }
24847     
24848     
24849     Roo.View.superclass.constructor.call(this);
24850     
24851     
24852 };
24853
24854 Roo.extend(Roo.View, Roo.util.Observable, {
24855     
24856      /**
24857      * @cfg {Roo.data.Store} store Data store to load data from.
24858      */
24859     store : false,
24860     
24861     /**
24862      * @cfg {String|Roo.Element} el The container element.
24863      */
24864     el : '',
24865     
24866     /**
24867      * @cfg {String|Roo.Template} tpl The template used by this View 
24868      */
24869     tpl : false,
24870     /**
24871      * @cfg {String} dataName the named area of the template to use as the data area
24872      *                          Works with domtemplates roo-name="name"
24873      */
24874     dataName: false,
24875     /**
24876      * @cfg {String} selectedClass The css class to add to selected nodes
24877      */
24878     selectedClass : "x-view-selected",
24879      /**
24880      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24881      */
24882     emptyText : "",
24883     
24884     /**
24885      * @cfg {String} text to display on mask (default Loading)
24886      */
24887     mask : false,
24888     /**
24889      * @cfg {Boolean} multiSelect Allow multiple selection
24890      */
24891     multiSelect : false,
24892     /**
24893      * @cfg {Boolean} singleSelect Allow single selection
24894      */
24895     singleSelect:  false,
24896     
24897     /**
24898      * @cfg {Boolean} toggleSelect - selecting 
24899      */
24900     toggleSelect : false,
24901     
24902     /**
24903      * Returns the element this view is bound to.
24904      * @return {Roo.Element}
24905      */
24906     getEl : function(){
24907         return this.wrapEl;
24908     },
24909     
24910     
24911
24912     /**
24913      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24914      */
24915     refresh : function(){
24916         var t = this.tpl;
24917         
24918         // if we are using something like 'domtemplate', then
24919         // the what gets used is:
24920         // t.applySubtemplate(NAME, data, wrapping data..)
24921         // the outer template then get' applied with
24922         //     the store 'extra data'
24923         // and the body get's added to the
24924         //      roo-name="data" node?
24925         //      <span class='roo-tpl-{name}'></span> ?????
24926         
24927         
24928         
24929         this.clearSelections();
24930         this.el.update("");
24931         var html = [];
24932         var records = this.store.getRange();
24933         if(records.length < 1) {
24934             
24935             // is this valid??  = should it render a template??
24936             
24937             this.el.update(this.emptyText);
24938             return;
24939         }
24940         var el = this.el;
24941         if (this.dataName) {
24942             this.el.update(t.apply(this.store.meta)); //????
24943             el = this.el.child('.roo-tpl-' + this.dataName);
24944         }
24945         
24946         for(var i = 0, len = records.length; i < len; i++){
24947             var data = this.prepareData(records[i].data, i, records[i]);
24948             this.fireEvent("preparedata", this, data, i, records[i]);
24949             html[html.length] = Roo.util.Format.trim(
24950                 this.dataName ?
24951                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24952                     t.apply(data)
24953             );
24954         }
24955         
24956         
24957         
24958         el.update(html.join(""));
24959         this.nodes = el.dom.childNodes;
24960         this.updateIndexes(0);
24961     },
24962
24963     /**
24964      * Function to override to reformat the data that is sent to
24965      * the template for each node.
24966      * DEPRICATED - use the preparedata event handler.
24967      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24968      * a JSON object for an UpdateManager bound view).
24969      */
24970     prepareData : function(data, index, record)
24971     {
24972         this.fireEvent("preparedata", this, data, index, record);
24973         return data;
24974     },
24975
24976     onUpdate : function(ds, record){
24977         this.clearSelections();
24978         var index = this.store.indexOf(record);
24979         var n = this.nodes[index];
24980         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24981         n.parentNode.removeChild(n);
24982         this.updateIndexes(index, index);
24983     },
24984
24985     
24986     
24987 // --------- FIXME     
24988     onAdd : function(ds, records, index)
24989     {
24990         this.clearSelections();
24991         if(this.nodes.length == 0){
24992             this.refresh();
24993             return;
24994         }
24995         var n = this.nodes[index];
24996         for(var i = 0, len = records.length; i < len; i++){
24997             var d = this.prepareData(records[i].data, i, records[i]);
24998             if(n){
24999                 this.tpl.insertBefore(n, d);
25000             }else{
25001                 
25002                 this.tpl.append(this.el, d);
25003             }
25004         }
25005         this.updateIndexes(index);
25006     },
25007
25008     onRemove : function(ds, record, index){
25009         this.clearSelections();
25010         var el = this.dataName  ?
25011             this.el.child('.roo-tpl-' + this.dataName) :
25012             this.el; 
25013         el.dom.removeChild(this.nodes[index]);
25014         this.updateIndexes(index);
25015     },
25016
25017     /**
25018      * Refresh an individual node.
25019      * @param {Number} index
25020      */
25021     refreshNode : function(index){
25022         this.onUpdate(this.store, this.store.getAt(index));
25023     },
25024
25025     updateIndexes : function(startIndex, endIndex){
25026         var ns = this.nodes;
25027         startIndex = startIndex || 0;
25028         endIndex = endIndex || ns.length - 1;
25029         for(var i = startIndex; i <= endIndex; i++){
25030             ns[i].nodeIndex = i;
25031         }
25032     },
25033
25034     /**
25035      * Changes the data store this view uses and refresh the view.
25036      * @param {Store} store
25037      */
25038     setStore : function(store, initial){
25039         if(!initial && this.store){
25040             this.store.un("datachanged", this.refresh);
25041             this.store.un("add", this.onAdd);
25042             this.store.un("remove", this.onRemove);
25043             this.store.un("update", this.onUpdate);
25044             this.store.un("clear", this.refresh);
25045             this.store.un("beforeload", this.onBeforeLoad);
25046             this.store.un("load", this.onLoad);
25047             this.store.un("loadexception", this.onLoad);
25048         }
25049         if(store){
25050           
25051             store.on("datachanged", this.refresh, this);
25052             store.on("add", this.onAdd, this);
25053             store.on("remove", this.onRemove, this);
25054             store.on("update", this.onUpdate, this);
25055             store.on("clear", this.refresh, this);
25056             store.on("beforeload", this.onBeforeLoad, this);
25057             store.on("load", this.onLoad, this);
25058             store.on("loadexception", this.onLoad, this);
25059         }
25060         
25061         if(store){
25062             this.refresh();
25063         }
25064     },
25065     /**
25066      * onbeforeLoad - masks the loading area.
25067      *
25068      */
25069     onBeforeLoad : function()
25070     {
25071         this.el.update("");
25072         this.el.mask(this.mask ? this.mask : "Loading" ); 
25073     },
25074     onLoad : function ()
25075     {
25076         this.el.unmask();
25077     },
25078     
25079
25080     /**
25081      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25082      * @param {HTMLElement} node
25083      * @return {HTMLElement} The template node
25084      */
25085     findItemFromChild : function(node){
25086         var el = this.dataName  ?
25087             this.el.child('.roo-tpl-' + this.dataName,true) :
25088             this.el.dom; 
25089         
25090         if(!node || node.parentNode == el){
25091                     return node;
25092             }
25093             var p = node.parentNode;
25094             while(p && p != el){
25095             if(p.parentNode == el){
25096                 return p;
25097             }
25098             p = p.parentNode;
25099         }
25100             return null;
25101     },
25102
25103     /** @ignore */
25104     onClick : function(e){
25105         var item = this.findItemFromChild(e.getTarget());
25106         if(item){
25107             var index = this.indexOf(item);
25108             if(this.onItemClick(item, index, e) !== false){
25109                 this.fireEvent("click", this, index, item, e);
25110             }
25111         }else{
25112             this.clearSelections();
25113         }
25114     },
25115
25116     /** @ignore */
25117     onContextMenu : function(e){
25118         var item = this.findItemFromChild(e.getTarget());
25119         if(item){
25120             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25121         }
25122     },
25123
25124     /** @ignore */
25125     onDblClick : function(e){
25126         var item = this.findItemFromChild(e.getTarget());
25127         if(item){
25128             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25129         }
25130     },
25131
25132     onItemClick : function(item, index, e)
25133     {
25134         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25135             return false;
25136         }
25137         if (this.toggleSelect) {
25138             var m = this.isSelected(item) ? 'unselect' : 'select';
25139             Roo.log(m);
25140             var _t = this;
25141             _t[m](item, true, false);
25142             return true;
25143         }
25144         if(this.multiSelect || this.singleSelect){
25145             if(this.multiSelect && e.shiftKey && this.lastSelection){
25146                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25147             }else{
25148                 this.select(item, this.multiSelect && e.ctrlKey);
25149                 this.lastSelection = item;
25150             }
25151             e.preventDefault();
25152         }
25153         return true;
25154     },
25155
25156     /**
25157      * Get the number of selected nodes.
25158      * @return {Number}
25159      */
25160     getSelectionCount : function(){
25161         return this.selections.length;
25162     },
25163
25164     /**
25165      * Get the currently selected nodes.
25166      * @return {Array} An array of HTMLElements
25167      */
25168     getSelectedNodes : function(){
25169         return this.selections;
25170     },
25171
25172     /**
25173      * Get the indexes of the selected nodes.
25174      * @return {Array}
25175      */
25176     getSelectedIndexes : function(){
25177         var indexes = [], s = this.selections;
25178         for(var i = 0, len = s.length; i < len; i++){
25179             indexes.push(s[i].nodeIndex);
25180         }
25181         return indexes;
25182     },
25183
25184     /**
25185      * Clear all selections
25186      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25187      */
25188     clearSelections : function(suppressEvent){
25189         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25190             this.cmp.elements = this.selections;
25191             this.cmp.removeClass(this.selectedClass);
25192             this.selections = [];
25193             if(!suppressEvent){
25194                 this.fireEvent("selectionchange", this, this.selections);
25195             }
25196         }
25197     },
25198
25199     /**
25200      * Returns true if the passed node is selected
25201      * @param {HTMLElement/Number} node The node or node index
25202      * @return {Boolean}
25203      */
25204     isSelected : function(node){
25205         var s = this.selections;
25206         if(s.length < 1){
25207             return false;
25208         }
25209         node = this.getNode(node);
25210         return s.indexOf(node) !== -1;
25211     },
25212
25213     /**
25214      * Selects nodes.
25215      * @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
25216      * @param {Boolean} keepExisting (optional) true to keep existing selections
25217      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25218      */
25219     select : function(nodeInfo, keepExisting, suppressEvent){
25220         if(nodeInfo instanceof Array){
25221             if(!keepExisting){
25222                 this.clearSelections(true);
25223             }
25224             for(var i = 0, len = nodeInfo.length; i < len; i++){
25225                 this.select(nodeInfo[i], true, true);
25226             }
25227             return;
25228         } 
25229         var node = this.getNode(nodeInfo);
25230         if(!node || this.isSelected(node)){
25231             return; // already selected.
25232         }
25233         if(!keepExisting){
25234             this.clearSelections(true);
25235         }
25236         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25237             Roo.fly(node).addClass(this.selectedClass);
25238             this.selections.push(node);
25239             if(!suppressEvent){
25240                 this.fireEvent("selectionchange", this, this.selections);
25241             }
25242         }
25243         
25244         
25245     },
25246       /**
25247      * Unselects nodes.
25248      * @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
25249      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25250      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25251      */
25252     unselect : function(nodeInfo, keepExisting, suppressEvent)
25253     {
25254         if(nodeInfo instanceof Array){
25255             Roo.each(this.selections, function(s) {
25256                 this.unselect(s, nodeInfo);
25257             }, this);
25258             return;
25259         }
25260         var node = this.getNode(nodeInfo);
25261         if(!node || !this.isSelected(node)){
25262             Roo.log("not selected");
25263             return; // not selected.
25264         }
25265         // fireevent???
25266         var ns = [];
25267         Roo.each(this.selections, function(s) {
25268             if (s == node ) {
25269                 Roo.fly(node).removeClass(this.selectedClass);
25270
25271                 return;
25272             }
25273             ns.push(s);
25274         },this);
25275         
25276         this.selections= ns;
25277         this.fireEvent("selectionchange", this, this.selections);
25278     },
25279
25280     /**
25281      * Gets a template node.
25282      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25283      * @return {HTMLElement} The node or null if it wasn't found
25284      */
25285     getNode : function(nodeInfo){
25286         if(typeof nodeInfo == "string"){
25287             return document.getElementById(nodeInfo);
25288         }else if(typeof nodeInfo == "number"){
25289             return this.nodes[nodeInfo];
25290         }
25291         return nodeInfo;
25292     },
25293
25294     /**
25295      * Gets a range template nodes.
25296      * @param {Number} startIndex
25297      * @param {Number} endIndex
25298      * @return {Array} An array of nodes
25299      */
25300     getNodes : function(start, end){
25301         var ns = this.nodes;
25302         start = start || 0;
25303         end = typeof end == "undefined" ? ns.length - 1 : end;
25304         var nodes = [];
25305         if(start <= end){
25306             for(var i = start; i <= end; i++){
25307                 nodes.push(ns[i]);
25308             }
25309         } else{
25310             for(var i = start; i >= end; i--){
25311                 nodes.push(ns[i]);
25312             }
25313         }
25314         return nodes;
25315     },
25316
25317     /**
25318      * Finds the index of the passed node
25319      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25320      * @return {Number} The index of the node or -1
25321      */
25322     indexOf : function(node){
25323         node = this.getNode(node);
25324         if(typeof node.nodeIndex == "number"){
25325             return node.nodeIndex;
25326         }
25327         var ns = this.nodes;
25328         for(var i = 0, len = ns.length; i < len; i++){
25329             if(ns[i] == node){
25330                 return i;
25331             }
25332         }
25333         return -1;
25334     }
25335 });
25336 /*
25337  * Based on:
25338  * Ext JS Library 1.1.1
25339  * Copyright(c) 2006-2007, Ext JS, LLC.
25340  *
25341  * Originally Released Under LGPL - original licence link has changed is not relivant.
25342  *
25343  * Fork - LGPL
25344  * <script type="text/javascript">
25345  */
25346
25347 /**
25348  * @class Roo.JsonView
25349  * @extends Roo.View
25350  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25351 <pre><code>
25352 var view = new Roo.JsonView({
25353     container: "my-element",
25354     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25355     multiSelect: true, 
25356     jsonRoot: "data" 
25357 });
25358
25359 // listen for node click?
25360 view.on("click", function(vw, index, node, e){
25361     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25362 });
25363
25364 // direct load of JSON data
25365 view.load("foobar.php");
25366
25367 // Example from my blog list
25368 var tpl = new Roo.Template(
25369     '&lt;div class="entry"&gt;' +
25370     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25371     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25372     "&lt;/div&gt;&lt;hr /&gt;"
25373 );
25374
25375 var moreView = new Roo.JsonView({
25376     container :  "entry-list", 
25377     template : tpl,
25378     jsonRoot: "posts"
25379 });
25380 moreView.on("beforerender", this.sortEntries, this);
25381 moreView.load({
25382     url: "/blog/get-posts.php",
25383     params: "allposts=true",
25384     text: "Loading Blog Entries..."
25385 });
25386 </code></pre>
25387
25388 * Note: old code is supported with arguments : (container, template, config)
25389
25390
25391  * @constructor
25392  * Create a new JsonView
25393  * 
25394  * @param {Object} config The config object
25395  * 
25396  */
25397 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25398     
25399     
25400     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25401
25402     var um = this.el.getUpdateManager();
25403     um.setRenderer(this);
25404     um.on("update", this.onLoad, this);
25405     um.on("failure", this.onLoadException, this);
25406
25407     /**
25408      * @event beforerender
25409      * Fires before rendering of the downloaded JSON data.
25410      * @param {Roo.JsonView} this
25411      * @param {Object} data The JSON data loaded
25412      */
25413     /**
25414      * @event load
25415      * Fires when data is loaded.
25416      * @param {Roo.JsonView} this
25417      * @param {Object} data The JSON data loaded
25418      * @param {Object} response The raw Connect response object
25419      */
25420     /**
25421      * @event loadexception
25422      * Fires when loading fails.
25423      * @param {Roo.JsonView} this
25424      * @param {Object} response The raw Connect response object
25425      */
25426     this.addEvents({
25427         'beforerender' : true,
25428         'load' : true,
25429         'loadexception' : true
25430     });
25431 };
25432 Roo.extend(Roo.JsonView, Roo.View, {
25433     /**
25434      * @type {String} The root property in the loaded JSON object that contains the data
25435      */
25436     jsonRoot : "",
25437
25438     /**
25439      * Refreshes the view.
25440      */
25441     refresh : function(){
25442         this.clearSelections();
25443         this.el.update("");
25444         var html = [];
25445         var o = this.jsonData;
25446         if(o && o.length > 0){
25447             for(var i = 0, len = o.length; i < len; i++){
25448                 var data = this.prepareData(o[i], i, o);
25449                 html[html.length] = this.tpl.apply(data);
25450             }
25451         }else{
25452             html.push(this.emptyText);
25453         }
25454         this.el.update(html.join(""));
25455         this.nodes = this.el.dom.childNodes;
25456         this.updateIndexes(0);
25457     },
25458
25459     /**
25460      * 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.
25461      * @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:
25462      <pre><code>
25463      view.load({
25464          url: "your-url.php",
25465          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25466          callback: yourFunction,
25467          scope: yourObject, //(optional scope)
25468          discardUrl: false,
25469          nocache: false,
25470          text: "Loading...",
25471          timeout: 30,
25472          scripts: false
25473      });
25474      </code></pre>
25475      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25476      * 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.
25477      * @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}
25478      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25479      * @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.
25480      */
25481     load : function(){
25482         var um = this.el.getUpdateManager();
25483         um.update.apply(um, arguments);
25484     },
25485
25486     render : function(el, response){
25487         this.clearSelections();
25488         this.el.update("");
25489         var o;
25490         try{
25491             o = Roo.util.JSON.decode(response.responseText);
25492             if(this.jsonRoot){
25493                 
25494                 o = o[this.jsonRoot];
25495             }
25496         } catch(e){
25497         }
25498         /**
25499          * The current JSON data or null
25500          */
25501         this.jsonData = o;
25502         this.beforeRender();
25503         this.refresh();
25504     },
25505
25506 /**
25507  * Get the number of records in the current JSON dataset
25508  * @return {Number}
25509  */
25510     getCount : function(){
25511         return this.jsonData ? this.jsonData.length : 0;
25512     },
25513
25514 /**
25515  * Returns the JSON object for the specified node(s)
25516  * @param {HTMLElement/Array} node The node or an array of nodes
25517  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25518  * you get the JSON object for the node
25519  */
25520     getNodeData : function(node){
25521         if(node instanceof Array){
25522             var data = [];
25523             for(var i = 0, len = node.length; i < len; i++){
25524                 data.push(this.getNodeData(node[i]));
25525             }
25526             return data;
25527         }
25528         return this.jsonData[this.indexOf(node)] || null;
25529     },
25530
25531     beforeRender : function(){
25532         this.snapshot = this.jsonData;
25533         if(this.sortInfo){
25534             this.sort.apply(this, this.sortInfo);
25535         }
25536         this.fireEvent("beforerender", this, this.jsonData);
25537     },
25538
25539     onLoad : function(el, o){
25540         this.fireEvent("load", this, this.jsonData, o);
25541     },
25542
25543     onLoadException : function(el, o){
25544         this.fireEvent("loadexception", this, o);
25545     },
25546
25547 /**
25548  * Filter the data by a specific property.
25549  * @param {String} property A property on your JSON objects
25550  * @param {String/RegExp} value Either string that the property values
25551  * should start with, or a RegExp to test against the property
25552  */
25553     filter : function(property, value){
25554         if(this.jsonData){
25555             var data = [];
25556             var ss = this.snapshot;
25557             if(typeof value == "string"){
25558                 var vlen = value.length;
25559                 if(vlen == 0){
25560                     this.clearFilter();
25561                     return;
25562                 }
25563                 value = value.toLowerCase();
25564                 for(var i = 0, len = ss.length; i < len; i++){
25565                     var o = ss[i];
25566                     if(o[property].substr(0, vlen).toLowerCase() == value){
25567                         data.push(o);
25568                     }
25569                 }
25570             } else if(value.exec){ // regex?
25571                 for(var i = 0, len = ss.length; i < len; i++){
25572                     var o = ss[i];
25573                     if(value.test(o[property])){
25574                         data.push(o);
25575                     }
25576                 }
25577             } else{
25578                 return;
25579             }
25580             this.jsonData = data;
25581             this.refresh();
25582         }
25583     },
25584
25585 /**
25586  * Filter by a function. The passed function will be called with each
25587  * object in the current dataset. If the function returns true the value is kept,
25588  * otherwise it is filtered.
25589  * @param {Function} fn
25590  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25591  */
25592     filterBy : function(fn, scope){
25593         if(this.jsonData){
25594             var data = [];
25595             var ss = this.snapshot;
25596             for(var i = 0, len = ss.length; i < len; i++){
25597                 var o = ss[i];
25598                 if(fn.call(scope || this, o)){
25599                     data.push(o);
25600                 }
25601             }
25602             this.jsonData = data;
25603             this.refresh();
25604         }
25605     },
25606
25607 /**
25608  * Clears the current filter.
25609  */
25610     clearFilter : function(){
25611         if(this.snapshot && this.jsonData != this.snapshot){
25612             this.jsonData = this.snapshot;
25613             this.refresh();
25614         }
25615     },
25616
25617
25618 /**
25619  * Sorts the data for this view and refreshes it.
25620  * @param {String} property A property on your JSON objects to sort on
25621  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25622  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25623  */
25624     sort : function(property, dir, sortType){
25625         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25626         if(this.jsonData){
25627             var p = property;
25628             var dsc = dir && dir.toLowerCase() == "desc";
25629             var f = function(o1, o2){
25630                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25631                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25632                 ;
25633                 if(v1 < v2){
25634                     return dsc ? +1 : -1;
25635                 } else if(v1 > v2){
25636                     return dsc ? -1 : +1;
25637                 } else{
25638                     return 0;
25639                 }
25640             };
25641             this.jsonData.sort(f);
25642             this.refresh();
25643             if(this.jsonData != this.snapshot){
25644                 this.snapshot.sort(f);
25645             }
25646         }
25647     }
25648 });/*
25649  * Based on:
25650  * Ext JS Library 1.1.1
25651  * Copyright(c) 2006-2007, Ext JS, LLC.
25652  *
25653  * Originally Released Under LGPL - original licence link has changed is not relivant.
25654  *
25655  * Fork - LGPL
25656  * <script type="text/javascript">
25657  */
25658  
25659
25660 /**
25661  * @class Roo.ColorPalette
25662  * @extends Roo.Component
25663  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25664  * Here's an example of typical usage:
25665  * <pre><code>
25666 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25667 cp.render('my-div');
25668
25669 cp.on('select', function(palette, selColor){
25670     // do something with selColor
25671 });
25672 </code></pre>
25673  * @constructor
25674  * Create a new ColorPalette
25675  * @param {Object} config The config object
25676  */
25677 Roo.ColorPalette = function(config){
25678     Roo.ColorPalette.superclass.constructor.call(this, config);
25679     this.addEvents({
25680         /**
25681              * @event select
25682              * Fires when a color is selected
25683              * @param {ColorPalette} this
25684              * @param {String} color The 6-digit color hex code (without the # symbol)
25685              */
25686         select: true
25687     });
25688
25689     if(this.handler){
25690         this.on("select", this.handler, this.scope, true);
25691     }
25692 };
25693 Roo.extend(Roo.ColorPalette, Roo.Component, {
25694     /**
25695      * @cfg {String} itemCls
25696      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25697      */
25698     itemCls : "x-color-palette",
25699     /**
25700      * @cfg {String} value
25701      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25702      * the hex codes are case-sensitive.
25703      */
25704     value : null,
25705     clickEvent:'click',
25706     // private
25707     ctype: "Roo.ColorPalette",
25708
25709     /**
25710      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25711      */
25712     allowReselect : false,
25713
25714     /**
25715      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25716      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25717      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25718      * of colors with the width setting until the box is symmetrical.</p>
25719      * <p>You can override individual colors if needed:</p>
25720      * <pre><code>
25721 var cp = new Roo.ColorPalette();
25722 cp.colors[0] = "FF0000";  // change the first box to red
25723 </code></pre>
25724
25725 Or you can provide a custom array of your own for complete control:
25726 <pre><code>
25727 var cp = new Roo.ColorPalette();
25728 cp.colors = ["000000", "993300", "333300"];
25729 </code></pre>
25730      * @type Array
25731      */
25732     colors : [
25733         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25734         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25735         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25736         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25737         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25738     ],
25739
25740     // private
25741     onRender : function(container, position){
25742         var t = new Roo.MasterTemplate(
25743             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25744         );
25745         var c = this.colors;
25746         for(var i = 0, len = c.length; i < len; i++){
25747             t.add([c[i]]);
25748         }
25749         var el = document.createElement("div");
25750         el.className = this.itemCls;
25751         t.overwrite(el);
25752         container.dom.insertBefore(el, position);
25753         this.el = Roo.get(el);
25754         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25755         if(this.clickEvent != 'click'){
25756             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25757         }
25758     },
25759
25760     // private
25761     afterRender : function(){
25762         Roo.ColorPalette.superclass.afterRender.call(this);
25763         if(this.value){
25764             var s = this.value;
25765             this.value = null;
25766             this.select(s);
25767         }
25768     },
25769
25770     // private
25771     handleClick : function(e, t){
25772         e.preventDefault();
25773         if(!this.disabled){
25774             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25775             this.select(c.toUpperCase());
25776         }
25777     },
25778
25779     /**
25780      * Selects the specified color in the palette (fires the select event)
25781      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25782      */
25783     select : function(color){
25784         color = color.replace("#", "");
25785         if(color != this.value || this.allowReselect){
25786             var el = this.el;
25787             if(this.value){
25788                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25789             }
25790             el.child("a.color-"+color).addClass("x-color-palette-sel");
25791             this.value = color;
25792             this.fireEvent("select", this, color);
25793         }
25794     }
25795 });/*
25796  * Based on:
25797  * Ext JS Library 1.1.1
25798  * Copyright(c) 2006-2007, Ext JS, LLC.
25799  *
25800  * Originally Released Under LGPL - original licence link has changed is not relivant.
25801  *
25802  * Fork - LGPL
25803  * <script type="text/javascript">
25804  */
25805  
25806 /**
25807  * @class Roo.DatePicker
25808  * @extends Roo.Component
25809  * Simple date picker class.
25810  * @constructor
25811  * Create a new DatePicker
25812  * @param {Object} config The config object
25813  */
25814 Roo.DatePicker = function(config){
25815     Roo.DatePicker.superclass.constructor.call(this, config);
25816
25817     this.value = config && config.value ?
25818                  config.value.clearTime() : new Date().clearTime();
25819
25820     this.addEvents({
25821         /**
25822              * @event select
25823              * Fires when a date is selected
25824              * @param {DatePicker} this
25825              * @param {Date} date The selected date
25826              */
25827         'select': true,
25828         /**
25829              * @event monthchange
25830              * Fires when the displayed month changes 
25831              * @param {DatePicker} this
25832              * @param {Date} date The selected month
25833              */
25834         'monthchange': true
25835     });
25836
25837     if(this.handler){
25838         this.on("select", this.handler,  this.scope || this);
25839     }
25840     // build the disabledDatesRE
25841     if(!this.disabledDatesRE && this.disabledDates){
25842         var dd = this.disabledDates;
25843         var re = "(?:";
25844         for(var i = 0; i < dd.length; i++){
25845             re += dd[i];
25846             if(i != dd.length-1) re += "|";
25847         }
25848         this.disabledDatesRE = new RegExp(re + ")");
25849     }
25850 };
25851
25852 Roo.extend(Roo.DatePicker, Roo.Component, {
25853     /**
25854      * @cfg {String} todayText
25855      * The text to display on the button that selects the current date (defaults to "Today")
25856      */
25857     todayText : "Today",
25858     /**
25859      * @cfg {String} okText
25860      * The text to display on the ok button
25861      */
25862     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25863     /**
25864      * @cfg {String} cancelText
25865      * The text to display on the cancel button
25866      */
25867     cancelText : "Cancel",
25868     /**
25869      * @cfg {String} todayTip
25870      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25871      */
25872     todayTip : "{0} (Spacebar)",
25873     /**
25874      * @cfg {Date} minDate
25875      * Minimum allowable date (JavaScript date object, defaults to null)
25876      */
25877     minDate : null,
25878     /**
25879      * @cfg {Date} maxDate
25880      * Maximum allowable date (JavaScript date object, defaults to null)
25881      */
25882     maxDate : null,
25883     /**
25884      * @cfg {String} minText
25885      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25886      */
25887     minText : "This date is before the minimum date",
25888     /**
25889      * @cfg {String} maxText
25890      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25891      */
25892     maxText : "This date is after the maximum date",
25893     /**
25894      * @cfg {String} format
25895      * The default date format string which can be overriden for localization support.  The format must be
25896      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25897      */
25898     format : "m/d/y",
25899     /**
25900      * @cfg {Array} disabledDays
25901      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25902      */
25903     disabledDays : null,
25904     /**
25905      * @cfg {String} disabledDaysText
25906      * The tooltip to display when the date falls on a disabled day (defaults to "")
25907      */
25908     disabledDaysText : "",
25909     /**
25910      * @cfg {RegExp} disabledDatesRE
25911      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25912      */
25913     disabledDatesRE : null,
25914     /**
25915      * @cfg {String} disabledDatesText
25916      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25917      */
25918     disabledDatesText : "",
25919     /**
25920      * @cfg {Boolean} constrainToViewport
25921      * True to constrain the date picker to the viewport (defaults to true)
25922      */
25923     constrainToViewport : true,
25924     /**
25925      * @cfg {Array} monthNames
25926      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25927      */
25928     monthNames : Date.monthNames,
25929     /**
25930      * @cfg {Array} dayNames
25931      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25932      */
25933     dayNames : Date.dayNames,
25934     /**
25935      * @cfg {String} nextText
25936      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25937      */
25938     nextText: 'Next Month (Control+Right)',
25939     /**
25940      * @cfg {String} prevText
25941      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25942      */
25943     prevText: 'Previous Month (Control+Left)',
25944     /**
25945      * @cfg {String} monthYearText
25946      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25947      */
25948     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25949     /**
25950      * @cfg {Number} startDay
25951      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25952      */
25953     startDay : 0,
25954     /**
25955      * @cfg {Bool} showClear
25956      * Show a clear button (usefull for date form elements that can be blank.)
25957      */
25958     
25959     showClear: false,
25960     
25961     /**
25962      * Sets the value of the date field
25963      * @param {Date} value The date to set
25964      */
25965     setValue : function(value){
25966         var old = this.value;
25967         
25968         if (typeof(value) == 'string') {
25969          
25970             value = Date.parseDate(value, this.format);
25971         }
25972         if (!value) {
25973             value = new Date();
25974         }
25975         
25976         this.value = value.clearTime(true);
25977         if(this.el){
25978             this.update(this.value);
25979         }
25980     },
25981
25982     /**
25983      * Gets the current selected value of the date field
25984      * @return {Date} The selected date
25985      */
25986     getValue : function(){
25987         return this.value;
25988     },
25989
25990     // private
25991     focus : function(){
25992         if(this.el){
25993             this.update(this.activeDate);
25994         }
25995     },
25996
25997     // privateval
25998     onRender : function(container, position){
25999         
26000         var m = [
26001              '<table cellspacing="0">',
26002                 '<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>',
26003                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26004         var dn = this.dayNames;
26005         for(var i = 0; i < 7; i++){
26006             var d = this.startDay+i;
26007             if(d > 6){
26008                 d = d-7;
26009             }
26010             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26011         }
26012         m[m.length] = "</tr></thead><tbody><tr>";
26013         for(var i = 0; i < 42; i++) {
26014             if(i % 7 == 0 && i != 0){
26015                 m[m.length] = "</tr><tr>";
26016             }
26017             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26018         }
26019         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26020             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26021
26022         var el = document.createElement("div");
26023         el.className = "x-date-picker";
26024         el.innerHTML = m.join("");
26025
26026         container.dom.insertBefore(el, position);
26027
26028         this.el = Roo.get(el);
26029         this.eventEl = Roo.get(el.firstChild);
26030
26031         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26032             handler: this.showPrevMonth,
26033             scope: this,
26034             preventDefault:true,
26035             stopDefault:true
26036         });
26037
26038         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26039             handler: this.showNextMonth,
26040             scope: this,
26041             preventDefault:true,
26042             stopDefault:true
26043         });
26044
26045         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26046
26047         this.monthPicker = this.el.down('div.x-date-mp');
26048         this.monthPicker.enableDisplayMode('block');
26049         
26050         var kn = new Roo.KeyNav(this.eventEl, {
26051             "left" : function(e){
26052                 e.ctrlKey ?
26053                     this.showPrevMonth() :
26054                     this.update(this.activeDate.add("d", -1));
26055             },
26056
26057             "right" : function(e){
26058                 e.ctrlKey ?
26059                     this.showNextMonth() :
26060                     this.update(this.activeDate.add("d", 1));
26061             },
26062
26063             "up" : function(e){
26064                 e.ctrlKey ?
26065                     this.showNextYear() :
26066                     this.update(this.activeDate.add("d", -7));
26067             },
26068
26069             "down" : function(e){
26070                 e.ctrlKey ?
26071                     this.showPrevYear() :
26072                     this.update(this.activeDate.add("d", 7));
26073             },
26074
26075             "pageUp" : function(e){
26076                 this.showNextMonth();
26077             },
26078
26079             "pageDown" : function(e){
26080                 this.showPrevMonth();
26081             },
26082
26083             "enter" : function(e){
26084                 e.stopPropagation();
26085                 return true;
26086             },
26087
26088             scope : this
26089         });
26090
26091         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26092
26093         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26094
26095         this.el.unselectable();
26096         
26097         this.cells = this.el.select("table.x-date-inner tbody td");
26098         this.textNodes = this.el.query("table.x-date-inner tbody span");
26099
26100         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26101             text: "&#160;",
26102             tooltip: this.monthYearText
26103         });
26104
26105         this.mbtn.on('click', this.showMonthPicker, this);
26106         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26107
26108
26109         var today = (new Date()).dateFormat(this.format);
26110         
26111         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26112         if (this.showClear) {
26113             baseTb.add( new Roo.Toolbar.Fill());
26114         }
26115         baseTb.add({
26116             text: String.format(this.todayText, today),
26117             tooltip: String.format(this.todayTip, today),
26118             handler: this.selectToday,
26119             scope: this
26120         });
26121         
26122         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26123             
26124         //});
26125         if (this.showClear) {
26126             
26127             baseTb.add( new Roo.Toolbar.Fill());
26128             baseTb.add({
26129                 text: '&#160;',
26130                 cls: 'x-btn-icon x-btn-clear',
26131                 handler: function() {
26132                     //this.value = '';
26133                     this.fireEvent("select", this, '');
26134                 },
26135                 scope: this
26136             });
26137         }
26138         
26139         
26140         if(Roo.isIE){
26141             this.el.repaint();
26142         }
26143         this.update(this.value);
26144     },
26145
26146     createMonthPicker : function(){
26147         if(!this.monthPicker.dom.firstChild){
26148             var buf = ['<table border="0" cellspacing="0">'];
26149             for(var i = 0; i < 6; i++){
26150                 buf.push(
26151                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26152                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26153                     i == 0 ?
26154                     '<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>' :
26155                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26156                 );
26157             }
26158             buf.push(
26159                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26160                     this.okText,
26161                     '</button><button type="button" class="x-date-mp-cancel">',
26162                     this.cancelText,
26163                     '</button></td></tr>',
26164                 '</table>'
26165             );
26166             this.monthPicker.update(buf.join(''));
26167             this.monthPicker.on('click', this.onMonthClick, this);
26168             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26169
26170             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26171             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26172
26173             this.mpMonths.each(function(m, a, i){
26174                 i += 1;
26175                 if((i%2) == 0){
26176                     m.dom.xmonth = 5 + Math.round(i * .5);
26177                 }else{
26178                     m.dom.xmonth = Math.round((i-1) * .5);
26179                 }
26180             });
26181         }
26182     },
26183
26184     showMonthPicker : function(){
26185         this.createMonthPicker();
26186         var size = this.el.getSize();
26187         this.monthPicker.setSize(size);
26188         this.monthPicker.child('table').setSize(size);
26189
26190         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26191         this.updateMPMonth(this.mpSelMonth);
26192         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26193         this.updateMPYear(this.mpSelYear);
26194
26195         this.monthPicker.slideIn('t', {duration:.2});
26196     },
26197
26198     updateMPYear : function(y){
26199         this.mpyear = y;
26200         var ys = this.mpYears.elements;
26201         for(var i = 1; i <= 10; i++){
26202             var td = ys[i-1], y2;
26203             if((i%2) == 0){
26204                 y2 = y + Math.round(i * .5);
26205                 td.firstChild.innerHTML = y2;
26206                 td.xyear = y2;
26207             }else{
26208                 y2 = y - (5-Math.round(i * .5));
26209                 td.firstChild.innerHTML = y2;
26210                 td.xyear = y2;
26211             }
26212             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26213         }
26214     },
26215
26216     updateMPMonth : function(sm){
26217         this.mpMonths.each(function(m, a, i){
26218             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26219         });
26220     },
26221
26222     selectMPMonth: function(m){
26223         
26224     },
26225
26226     onMonthClick : function(e, t){
26227         e.stopEvent();
26228         var el = new Roo.Element(t), pn;
26229         if(el.is('button.x-date-mp-cancel')){
26230             this.hideMonthPicker();
26231         }
26232         else if(el.is('button.x-date-mp-ok')){
26233             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26234             this.hideMonthPicker();
26235         }
26236         else if(pn = el.up('td.x-date-mp-month', 2)){
26237             this.mpMonths.removeClass('x-date-mp-sel');
26238             pn.addClass('x-date-mp-sel');
26239             this.mpSelMonth = pn.dom.xmonth;
26240         }
26241         else if(pn = el.up('td.x-date-mp-year', 2)){
26242             this.mpYears.removeClass('x-date-mp-sel');
26243             pn.addClass('x-date-mp-sel');
26244             this.mpSelYear = pn.dom.xyear;
26245         }
26246         else if(el.is('a.x-date-mp-prev')){
26247             this.updateMPYear(this.mpyear-10);
26248         }
26249         else if(el.is('a.x-date-mp-next')){
26250             this.updateMPYear(this.mpyear+10);
26251         }
26252     },
26253
26254     onMonthDblClick : function(e, t){
26255         e.stopEvent();
26256         var el = new Roo.Element(t), pn;
26257         if(pn = el.up('td.x-date-mp-month', 2)){
26258             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26259             this.hideMonthPicker();
26260         }
26261         else if(pn = el.up('td.x-date-mp-year', 2)){
26262             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26263             this.hideMonthPicker();
26264         }
26265     },
26266
26267     hideMonthPicker : function(disableAnim){
26268         if(this.monthPicker){
26269             if(disableAnim === true){
26270                 this.monthPicker.hide();
26271             }else{
26272                 this.monthPicker.slideOut('t', {duration:.2});
26273             }
26274         }
26275     },
26276
26277     // private
26278     showPrevMonth : function(e){
26279         this.update(this.activeDate.add("mo", -1));
26280     },
26281
26282     // private
26283     showNextMonth : function(e){
26284         this.update(this.activeDate.add("mo", 1));
26285     },
26286
26287     // private
26288     showPrevYear : function(){
26289         this.update(this.activeDate.add("y", -1));
26290     },
26291
26292     // private
26293     showNextYear : function(){
26294         this.update(this.activeDate.add("y", 1));
26295     },
26296
26297     // private
26298     handleMouseWheel : function(e){
26299         var delta = e.getWheelDelta();
26300         if(delta > 0){
26301             this.showPrevMonth();
26302             e.stopEvent();
26303         } else if(delta < 0){
26304             this.showNextMonth();
26305             e.stopEvent();
26306         }
26307     },
26308
26309     // private
26310     handleDateClick : function(e, t){
26311         e.stopEvent();
26312         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26313             this.setValue(new Date(t.dateValue));
26314             this.fireEvent("select", this, this.value);
26315         }
26316     },
26317
26318     // private
26319     selectToday : function(){
26320         this.setValue(new Date().clearTime());
26321         this.fireEvent("select", this, this.value);
26322     },
26323
26324     // private
26325     update : function(date)
26326     {
26327         var vd = this.activeDate;
26328         this.activeDate = date;
26329         if(vd && this.el){
26330             var t = date.getTime();
26331             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26332                 this.cells.removeClass("x-date-selected");
26333                 this.cells.each(function(c){
26334                    if(c.dom.firstChild.dateValue == t){
26335                        c.addClass("x-date-selected");
26336                        setTimeout(function(){
26337                             try{c.dom.firstChild.focus();}catch(e){}
26338                        }, 50);
26339                        return false;
26340                    }
26341                 });
26342                 return;
26343             }
26344         }
26345         
26346         var days = date.getDaysInMonth();
26347         var firstOfMonth = date.getFirstDateOfMonth();
26348         var startingPos = firstOfMonth.getDay()-this.startDay;
26349
26350         if(startingPos <= this.startDay){
26351             startingPos += 7;
26352         }
26353
26354         var pm = date.add("mo", -1);
26355         var prevStart = pm.getDaysInMonth()-startingPos;
26356
26357         var cells = this.cells.elements;
26358         var textEls = this.textNodes;
26359         days += startingPos;
26360
26361         // convert everything to numbers so it's fast
26362         var day = 86400000;
26363         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26364         var today = new Date().clearTime().getTime();
26365         var sel = date.clearTime().getTime();
26366         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26367         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26368         var ddMatch = this.disabledDatesRE;
26369         var ddText = this.disabledDatesText;
26370         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26371         var ddaysText = this.disabledDaysText;
26372         var format = this.format;
26373
26374         var setCellClass = function(cal, cell){
26375             cell.title = "";
26376             var t = d.getTime();
26377             cell.firstChild.dateValue = t;
26378             if(t == today){
26379                 cell.className += " x-date-today";
26380                 cell.title = cal.todayText;
26381             }
26382             if(t == sel){
26383                 cell.className += " x-date-selected";
26384                 setTimeout(function(){
26385                     try{cell.firstChild.focus();}catch(e){}
26386                 }, 50);
26387             }
26388             // disabling
26389             if(t < min) {
26390                 cell.className = " x-date-disabled";
26391                 cell.title = cal.minText;
26392                 return;
26393             }
26394             if(t > max) {
26395                 cell.className = " x-date-disabled";
26396                 cell.title = cal.maxText;
26397                 return;
26398             }
26399             if(ddays){
26400                 if(ddays.indexOf(d.getDay()) != -1){
26401                     cell.title = ddaysText;
26402                     cell.className = " x-date-disabled";
26403                 }
26404             }
26405             if(ddMatch && format){
26406                 var fvalue = d.dateFormat(format);
26407                 if(ddMatch.test(fvalue)){
26408                     cell.title = ddText.replace("%0", fvalue);
26409                     cell.className = " x-date-disabled";
26410                 }
26411             }
26412         };
26413
26414         var i = 0;
26415         for(; i < startingPos; i++) {
26416             textEls[i].innerHTML = (++prevStart);
26417             d.setDate(d.getDate()+1);
26418             cells[i].className = "x-date-prevday";
26419             setCellClass(this, cells[i]);
26420         }
26421         for(; i < days; i++){
26422             intDay = i - startingPos + 1;
26423             textEls[i].innerHTML = (intDay);
26424             d.setDate(d.getDate()+1);
26425             cells[i].className = "x-date-active";
26426             setCellClass(this, cells[i]);
26427         }
26428         var extraDays = 0;
26429         for(; i < 42; i++) {
26430              textEls[i].innerHTML = (++extraDays);
26431              d.setDate(d.getDate()+1);
26432              cells[i].className = "x-date-nextday";
26433              setCellClass(this, cells[i]);
26434         }
26435
26436         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26437         this.fireEvent('monthchange', this, date);
26438         
26439         if(!this.internalRender){
26440             var main = this.el.dom.firstChild;
26441             var w = main.offsetWidth;
26442             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26443             Roo.fly(main).setWidth(w);
26444             this.internalRender = true;
26445             // opera does not respect the auto grow header center column
26446             // then, after it gets a width opera refuses to recalculate
26447             // without a second pass
26448             if(Roo.isOpera && !this.secondPass){
26449                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26450                 this.secondPass = true;
26451                 this.update.defer(10, this, [date]);
26452             }
26453         }
26454         
26455         
26456     }
26457 });        /*
26458  * Based on:
26459  * Ext JS Library 1.1.1
26460  * Copyright(c) 2006-2007, Ext JS, LLC.
26461  *
26462  * Originally Released Under LGPL - original licence link has changed is not relivant.
26463  *
26464  * Fork - LGPL
26465  * <script type="text/javascript">
26466  */
26467 /**
26468  * @class Roo.TabPanel
26469  * @extends Roo.util.Observable
26470  * A lightweight tab container.
26471  * <br><br>
26472  * Usage:
26473  * <pre><code>
26474 // basic tabs 1, built from existing content
26475 var tabs = new Roo.TabPanel("tabs1");
26476 tabs.addTab("script", "View Script");
26477 tabs.addTab("markup", "View Markup");
26478 tabs.activate("script");
26479
26480 // more advanced tabs, built from javascript
26481 var jtabs = new Roo.TabPanel("jtabs");
26482 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26483
26484 // set up the UpdateManager
26485 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26486 var updater = tab2.getUpdateManager();
26487 updater.setDefaultUrl("ajax1.htm");
26488 tab2.on('activate', updater.refresh, updater, true);
26489
26490 // Use setUrl for Ajax loading
26491 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26492 tab3.setUrl("ajax2.htm", null, true);
26493
26494 // Disabled tab
26495 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26496 tab4.disable();
26497
26498 jtabs.activate("jtabs-1");
26499  * </code></pre>
26500  * @constructor
26501  * Create a new TabPanel.
26502  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26503  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26504  */
26505 Roo.TabPanel = function(container, config){
26506     /**
26507     * The container element for this TabPanel.
26508     * @type Roo.Element
26509     */
26510     this.el = Roo.get(container, true);
26511     if(config){
26512         if(typeof config == "boolean"){
26513             this.tabPosition = config ? "bottom" : "top";
26514         }else{
26515             Roo.apply(this, config);
26516         }
26517     }
26518     if(this.tabPosition == "bottom"){
26519         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26520         this.el.addClass("x-tabs-bottom");
26521     }
26522     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26523     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26524     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26525     if(Roo.isIE){
26526         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26527     }
26528     if(this.tabPosition != "bottom"){
26529         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26530          * @type Roo.Element
26531          */
26532         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26533         this.el.addClass("x-tabs-top");
26534     }
26535     this.items = [];
26536
26537     this.bodyEl.setStyle("position", "relative");
26538
26539     this.active = null;
26540     this.activateDelegate = this.activate.createDelegate(this);
26541
26542     this.addEvents({
26543         /**
26544          * @event tabchange
26545          * Fires when the active tab changes
26546          * @param {Roo.TabPanel} this
26547          * @param {Roo.TabPanelItem} activePanel The new active tab
26548          */
26549         "tabchange": true,
26550         /**
26551          * @event beforetabchange
26552          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26553          * @param {Roo.TabPanel} this
26554          * @param {Object} e Set cancel to true on this object to cancel the tab change
26555          * @param {Roo.TabPanelItem} tab The tab being changed to
26556          */
26557         "beforetabchange" : true
26558     });
26559
26560     Roo.EventManager.onWindowResize(this.onResize, this);
26561     this.cpad = this.el.getPadding("lr");
26562     this.hiddenCount = 0;
26563
26564
26565     // toolbar on the tabbar support...
26566     if (this.toolbar) {
26567         var tcfg = this.toolbar;
26568         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26569         this.toolbar = new Roo.Toolbar(tcfg);
26570         if (Roo.isSafari) {
26571             var tbl = tcfg.container.child('table', true);
26572             tbl.setAttribute('width', '100%');
26573         }
26574         
26575     }
26576    
26577
26578
26579     Roo.TabPanel.superclass.constructor.call(this);
26580 };
26581
26582 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26583     /*
26584      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26585      */
26586     tabPosition : "top",
26587     /*
26588      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26589      */
26590     currentTabWidth : 0,
26591     /*
26592      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26593      */
26594     minTabWidth : 40,
26595     /*
26596      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26597      */
26598     maxTabWidth : 250,
26599     /*
26600      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26601      */
26602     preferredTabWidth : 175,
26603     /*
26604      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26605      */
26606     resizeTabs : false,
26607     /*
26608      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26609      */
26610     monitorResize : true,
26611     /*
26612      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26613      */
26614     toolbar : false,
26615
26616     /**
26617      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26618      * @param {String} id The id of the div to use <b>or create</b>
26619      * @param {String} text The text for the tab
26620      * @param {String} content (optional) Content to put in the TabPanelItem body
26621      * @param {Boolean} closable (optional) True to create a close icon on the tab
26622      * @return {Roo.TabPanelItem} The created TabPanelItem
26623      */
26624     addTab : function(id, text, content, closable){
26625         var item = new Roo.TabPanelItem(this, id, text, closable);
26626         this.addTabItem(item);
26627         if(content){
26628             item.setContent(content);
26629         }
26630         return item;
26631     },
26632
26633     /**
26634      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26635      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26636      * @return {Roo.TabPanelItem}
26637      */
26638     getTab : function(id){
26639         return this.items[id];
26640     },
26641
26642     /**
26643      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26644      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26645      */
26646     hideTab : function(id){
26647         var t = this.items[id];
26648         if(!t.isHidden()){
26649            t.setHidden(true);
26650            this.hiddenCount++;
26651            this.autoSizeTabs();
26652         }
26653     },
26654
26655     /**
26656      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26657      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26658      */
26659     unhideTab : function(id){
26660         var t = this.items[id];
26661         if(t.isHidden()){
26662            t.setHidden(false);
26663            this.hiddenCount--;
26664            this.autoSizeTabs();
26665         }
26666     },
26667
26668     /**
26669      * Adds an existing {@link Roo.TabPanelItem}.
26670      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26671      */
26672     addTabItem : function(item){
26673         this.items[item.id] = item;
26674         this.items.push(item);
26675         if(this.resizeTabs){
26676            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26677            this.autoSizeTabs();
26678         }else{
26679             item.autoSize();
26680         }
26681     },
26682
26683     /**
26684      * Removes a {@link Roo.TabPanelItem}.
26685      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26686      */
26687     removeTab : function(id){
26688         var items = this.items;
26689         var tab = items[id];
26690         if(!tab) { return; }
26691         var index = items.indexOf(tab);
26692         if(this.active == tab && items.length > 1){
26693             var newTab = this.getNextAvailable(index);
26694             if(newTab) {
26695                 newTab.activate();
26696             }
26697         }
26698         this.stripEl.dom.removeChild(tab.pnode.dom);
26699         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26700             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26701         }
26702         items.splice(index, 1);
26703         delete this.items[tab.id];
26704         tab.fireEvent("close", tab);
26705         tab.purgeListeners();
26706         this.autoSizeTabs();
26707     },
26708
26709     getNextAvailable : function(start){
26710         var items = this.items;
26711         var index = start;
26712         // look for a next tab that will slide over to
26713         // replace the one being removed
26714         while(index < items.length){
26715             var item = items[++index];
26716             if(item && !item.isHidden()){
26717                 return item;
26718             }
26719         }
26720         // if one isn't found select the previous tab (on the left)
26721         index = start;
26722         while(index >= 0){
26723             var item = items[--index];
26724             if(item && !item.isHidden()){
26725                 return item;
26726             }
26727         }
26728         return null;
26729     },
26730
26731     /**
26732      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26733      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26734      */
26735     disableTab : function(id){
26736         var tab = this.items[id];
26737         if(tab && this.active != tab){
26738             tab.disable();
26739         }
26740     },
26741
26742     /**
26743      * Enables a {@link Roo.TabPanelItem} that is disabled.
26744      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26745      */
26746     enableTab : function(id){
26747         var tab = this.items[id];
26748         tab.enable();
26749     },
26750
26751     /**
26752      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26753      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26754      * @return {Roo.TabPanelItem} The TabPanelItem.
26755      */
26756     activate : function(id){
26757         var tab = this.items[id];
26758         if(!tab){
26759             return null;
26760         }
26761         if(tab == this.active || tab.disabled){
26762             return tab;
26763         }
26764         var e = {};
26765         this.fireEvent("beforetabchange", this, e, tab);
26766         if(e.cancel !== true && !tab.disabled){
26767             if(this.active){
26768                 this.active.hide();
26769             }
26770             this.active = this.items[id];
26771             this.active.show();
26772             this.fireEvent("tabchange", this, this.active);
26773         }
26774         return tab;
26775     },
26776
26777     /**
26778      * Gets the active {@link Roo.TabPanelItem}.
26779      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26780      */
26781     getActiveTab : function(){
26782         return this.active;
26783     },
26784
26785     /**
26786      * Updates the tab body element to fit the height of the container element
26787      * for overflow scrolling
26788      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26789      */
26790     syncHeight : function(targetHeight){
26791         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26792         var bm = this.bodyEl.getMargins();
26793         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26794         this.bodyEl.setHeight(newHeight);
26795         return newHeight;
26796     },
26797
26798     onResize : function(){
26799         if(this.monitorResize){
26800             this.autoSizeTabs();
26801         }
26802     },
26803
26804     /**
26805      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26806      */
26807     beginUpdate : function(){
26808         this.updating = true;
26809     },
26810
26811     /**
26812      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26813      */
26814     endUpdate : function(){
26815         this.updating = false;
26816         this.autoSizeTabs();
26817     },
26818
26819     /**
26820      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26821      */
26822     autoSizeTabs : function(){
26823         var count = this.items.length;
26824         var vcount = count - this.hiddenCount;
26825         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26826         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26827         var availWidth = Math.floor(w / vcount);
26828         var b = this.stripBody;
26829         if(b.getWidth() > w){
26830             var tabs = this.items;
26831             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26832             if(availWidth < this.minTabWidth){
26833                 /*if(!this.sleft){    // incomplete scrolling code
26834                     this.createScrollButtons();
26835                 }
26836                 this.showScroll();
26837                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26838             }
26839         }else{
26840             if(this.currentTabWidth < this.preferredTabWidth){
26841                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26842             }
26843         }
26844     },
26845
26846     /**
26847      * Returns the number of tabs in this TabPanel.
26848      * @return {Number}
26849      */
26850      getCount : function(){
26851          return this.items.length;
26852      },
26853
26854     /**
26855      * Resizes all the tabs to the passed width
26856      * @param {Number} The new width
26857      */
26858     setTabWidth : function(width){
26859         this.currentTabWidth = width;
26860         for(var i = 0, len = this.items.length; i < len; i++) {
26861                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26862         }
26863     },
26864
26865     /**
26866      * Destroys this TabPanel
26867      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26868      */
26869     destroy : function(removeEl){
26870         Roo.EventManager.removeResizeListener(this.onResize, this);
26871         for(var i = 0, len = this.items.length; i < len; i++){
26872             this.items[i].purgeListeners();
26873         }
26874         if(removeEl === true){
26875             this.el.update("");
26876             this.el.remove();
26877         }
26878     }
26879 });
26880
26881 /**
26882  * @class Roo.TabPanelItem
26883  * @extends Roo.util.Observable
26884  * Represents an individual item (tab plus body) in a TabPanel.
26885  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26886  * @param {String} id The id of this TabPanelItem
26887  * @param {String} text The text for the tab of this TabPanelItem
26888  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26889  */
26890 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26891     /**
26892      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26893      * @type Roo.TabPanel
26894      */
26895     this.tabPanel = tabPanel;
26896     /**
26897      * The id for this TabPanelItem
26898      * @type String
26899      */
26900     this.id = id;
26901     /** @private */
26902     this.disabled = false;
26903     /** @private */
26904     this.text = text;
26905     /** @private */
26906     this.loaded = false;
26907     this.closable = closable;
26908
26909     /**
26910      * The body element for this TabPanelItem.
26911      * @type Roo.Element
26912      */
26913     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26914     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26915     this.bodyEl.setStyle("display", "block");
26916     this.bodyEl.setStyle("zoom", "1");
26917     this.hideAction();
26918
26919     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26920     /** @private */
26921     this.el = Roo.get(els.el, true);
26922     this.inner = Roo.get(els.inner, true);
26923     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26924     this.pnode = Roo.get(els.el.parentNode, true);
26925     this.el.on("mousedown", this.onTabMouseDown, this);
26926     this.el.on("click", this.onTabClick, this);
26927     /** @private */
26928     if(closable){
26929         var c = Roo.get(els.close, true);
26930         c.dom.title = this.closeText;
26931         c.addClassOnOver("close-over");
26932         c.on("click", this.closeClick, this);
26933      }
26934
26935     this.addEvents({
26936          /**
26937          * @event activate
26938          * Fires when this tab becomes the active tab.
26939          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26940          * @param {Roo.TabPanelItem} this
26941          */
26942         "activate": true,
26943         /**
26944          * @event beforeclose
26945          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26946          * @param {Roo.TabPanelItem} this
26947          * @param {Object} e Set cancel to true on this object to cancel the close.
26948          */
26949         "beforeclose": true,
26950         /**
26951          * @event close
26952          * Fires when this tab is closed.
26953          * @param {Roo.TabPanelItem} this
26954          */
26955          "close": true,
26956         /**
26957          * @event deactivate
26958          * Fires when this tab is no longer the active tab.
26959          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26960          * @param {Roo.TabPanelItem} this
26961          */
26962          "deactivate" : true
26963     });
26964     this.hidden = false;
26965
26966     Roo.TabPanelItem.superclass.constructor.call(this);
26967 };
26968
26969 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26970     purgeListeners : function(){
26971        Roo.util.Observable.prototype.purgeListeners.call(this);
26972        this.el.removeAllListeners();
26973     },
26974     /**
26975      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26976      */
26977     show : function(){
26978         this.pnode.addClass("on");
26979         this.showAction();
26980         if(Roo.isOpera){
26981             this.tabPanel.stripWrap.repaint();
26982         }
26983         this.fireEvent("activate", this.tabPanel, this);
26984     },
26985
26986     /**
26987      * Returns true if this tab is the active tab.
26988      * @return {Boolean}
26989      */
26990     isActive : function(){
26991         return this.tabPanel.getActiveTab() == this;
26992     },
26993
26994     /**
26995      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26996      */
26997     hide : function(){
26998         this.pnode.removeClass("on");
26999         this.hideAction();
27000         this.fireEvent("deactivate", this.tabPanel, this);
27001     },
27002
27003     hideAction : function(){
27004         this.bodyEl.hide();
27005         this.bodyEl.setStyle("position", "absolute");
27006         this.bodyEl.setLeft("-20000px");
27007         this.bodyEl.setTop("-20000px");
27008     },
27009
27010     showAction : function(){
27011         this.bodyEl.setStyle("position", "relative");
27012         this.bodyEl.setTop("");
27013         this.bodyEl.setLeft("");
27014         this.bodyEl.show();
27015     },
27016
27017     /**
27018      * Set the tooltip for the tab.
27019      * @param {String} tooltip The tab's tooltip
27020      */
27021     setTooltip : function(text){
27022         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27023             this.textEl.dom.qtip = text;
27024             this.textEl.dom.removeAttribute('title');
27025         }else{
27026             this.textEl.dom.title = text;
27027         }
27028     },
27029
27030     onTabClick : function(e){
27031         e.preventDefault();
27032         this.tabPanel.activate(this.id);
27033     },
27034
27035     onTabMouseDown : function(e){
27036         e.preventDefault();
27037         this.tabPanel.activate(this.id);
27038     },
27039
27040     getWidth : function(){
27041         return this.inner.getWidth();
27042     },
27043
27044     setWidth : function(width){
27045         var iwidth = width - this.pnode.getPadding("lr");
27046         this.inner.setWidth(iwidth);
27047         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27048         this.pnode.setWidth(width);
27049     },
27050
27051     /**
27052      * Show or hide the tab
27053      * @param {Boolean} hidden True to hide or false to show.
27054      */
27055     setHidden : function(hidden){
27056         this.hidden = hidden;
27057         this.pnode.setStyle("display", hidden ? "none" : "");
27058     },
27059
27060     /**
27061      * Returns true if this tab is "hidden"
27062      * @return {Boolean}
27063      */
27064     isHidden : function(){
27065         return this.hidden;
27066     },
27067
27068     /**
27069      * Returns the text for this tab
27070      * @return {String}
27071      */
27072     getText : function(){
27073         return this.text;
27074     },
27075
27076     autoSize : function(){
27077         //this.el.beginMeasure();
27078         this.textEl.setWidth(1);
27079         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27080         //this.el.endMeasure();
27081     },
27082
27083     /**
27084      * Sets the text for the tab (Note: this also sets the tooltip text)
27085      * @param {String} text The tab's text and tooltip
27086      */
27087     setText : function(text){
27088         this.text = text;
27089         this.textEl.update(text);
27090         this.setTooltip(text);
27091         if(!this.tabPanel.resizeTabs){
27092             this.autoSize();
27093         }
27094     },
27095     /**
27096      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27097      */
27098     activate : function(){
27099         this.tabPanel.activate(this.id);
27100     },
27101
27102     /**
27103      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27104      */
27105     disable : function(){
27106         if(this.tabPanel.active != this){
27107             this.disabled = true;
27108             this.pnode.addClass("disabled");
27109         }
27110     },
27111
27112     /**
27113      * Enables this TabPanelItem if it was previously disabled.
27114      */
27115     enable : function(){
27116         this.disabled = false;
27117         this.pnode.removeClass("disabled");
27118     },
27119
27120     /**
27121      * Sets the content for this TabPanelItem.
27122      * @param {String} content The content
27123      * @param {Boolean} loadScripts true to look for and load scripts
27124      */
27125     setContent : function(content, loadScripts){
27126         this.bodyEl.update(content, loadScripts);
27127     },
27128
27129     /**
27130      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27131      * @return {Roo.UpdateManager} The UpdateManager
27132      */
27133     getUpdateManager : function(){
27134         return this.bodyEl.getUpdateManager();
27135     },
27136
27137     /**
27138      * Set a URL to be used to load the content for this TabPanelItem.
27139      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27140      * @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)
27141      * @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)
27142      * @return {Roo.UpdateManager} The UpdateManager
27143      */
27144     setUrl : function(url, params, loadOnce){
27145         if(this.refreshDelegate){
27146             this.un('activate', this.refreshDelegate);
27147         }
27148         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27149         this.on("activate", this.refreshDelegate);
27150         return this.bodyEl.getUpdateManager();
27151     },
27152
27153     /** @private */
27154     _handleRefresh : function(url, params, loadOnce){
27155         if(!loadOnce || !this.loaded){
27156             var updater = this.bodyEl.getUpdateManager();
27157             updater.update(url, params, this._setLoaded.createDelegate(this));
27158         }
27159     },
27160
27161     /**
27162      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27163      *   Will fail silently if the setUrl method has not been called.
27164      *   This does not activate the panel, just updates its content.
27165      */
27166     refresh : function(){
27167         if(this.refreshDelegate){
27168            this.loaded = false;
27169            this.refreshDelegate();
27170         }
27171     },
27172
27173     /** @private */
27174     _setLoaded : function(){
27175         this.loaded = true;
27176     },
27177
27178     /** @private */
27179     closeClick : function(e){
27180         var o = {};
27181         e.stopEvent();
27182         this.fireEvent("beforeclose", this, o);
27183         if(o.cancel !== true){
27184             this.tabPanel.removeTab(this.id);
27185         }
27186     },
27187     /**
27188      * The text displayed in the tooltip for the close icon.
27189      * @type String
27190      */
27191     closeText : "Close this tab"
27192 });
27193
27194 /** @private */
27195 Roo.TabPanel.prototype.createStrip = function(container){
27196     var strip = document.createElement("div");
27197     strip.className = "x-tabs-wrap";
27198     container.appendChild(strip);
27199     return strip;
27200 };
27201 /** @private */
27202 Roo.TabPanel.prototype.createStripList = function(strip){
27203     // div wrapper for retard IE
27204     // returns the "tr" element.
27205     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27206         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27207         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27208     return strip.firstChild.firstChild.firstChild.firstChild;
27209 };
27210 /** @private */
27211 Roo.TabPanel.prototype.createBody = function(container){
27212     var body = document.createElement("div");
27213     Roo.id(body, "tab-body");
27214     Roo.fly(body).addClass("x-tabs-body");
27215     container.appendChild(body);
27216     return body;
27217 };
27218 /** @private */
27219 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27220     var body = Roo.getDom(id);
27221     if(!body){
27222         body = document.createElement("div");
27223         body.id = id;
27224     }
27225     Roo.fly(body).addClass("x-tabs-item-body");
27226     bodyEl.insertBefore(body, bodyEl.firstChild);
27227     return body;
27228 };
27229 /** @private */
27230 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27231     var td = document.createElement("td");
27232     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27233     //stripEl.appendChild(td);
27234     if(closable){
27235         td.className = "x-tabs-closable";
27236         if(!this.closeTpl){
27237             this.closeTpl = new Roo.Template(
27238                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27239                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27240                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27241             );
27242         }
27243         var el = this.closeTpl.overwrite(td, {"text": text});
27244         var close = el.getElementsByTagName("div")[0];
27245         var inner = el.getElementsByTagName("em")[0];
27246         return {"el": el, "close": close, "inner": inner};
27247     } else {
27248         if(!this.tabTpl){
27249             this.tabTpl = new Roo.Template(
27250                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27251                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27252             );
27253         }
27254         var el = this.tabTpl.overwrite(td, {"text": text});
27255         var inner = el.getElementsByTagName("em")[0];
27256         return {"el": el, "inner": inner};
27257     }
27258 };/*
27259  * Based on:
27260  * Ext JS Library 1.1.1
27261  * Copyright(c) 2006-2007, Ext JS, LLC.
27262  *
27263  * Originally Released Under LGPL - original licence link has changed is not relivant.
27264  *
27265  * Fork - LGPL
27266  * <script type="text/javascript">
27267  */
27268
27269 /**
27270  * @class Roo.Button
27271  * @extends Roo.util.Observable
27272  * Simple Button class
27273  * @cfg {String} text The button text
27274  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27275  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27276  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27277  * @cfg {Object} scope The scope of the handler
27278  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27279  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27280  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27281  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27282  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27283  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27284    applies if enableToggle = true)
27285  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27286  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27287   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27288  * @constructor
27289  * Create a new button
27290  * @param {Object} config The config object
27291  */
27292 Roo.Button = function(renderTo, config)
27293 {
27294     if (!config) {
27295         config = renderTo;
27296         renderTo = config.renderTo || false;
27297     }
27298     
27299     Roo.apply(this, config);
27300     this.addEvents({
27301         /**
27302              * @event click
27303              * Fires when this button is clicked
27304              * @param {Button} this
27305              * @param {EventObject} e The click event
27306              */
27307             "click" : true,
27308         /**
27309              * @event toggle
27310              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27311              * @param {Button} this
27312              * @param {Boolean} pressed
27313              */
27314             "toggle" : true,
27315         /**
27316              * @event mouseover
27317              * Fires when the mouse hovers over the button
27318              * @param {Button} this
27319              * @param {Event} e The event object
27320              */
27321         'mouseover' : true,
27322         /**
27323              * @event mouseout
27324              * Fires when the mouse exits the button
27325              * @param {Button} this
27326              * @param {Event} e The event object
27327              */
27328         'mouseout': true,
27329          /**
27330              * @event render
27331              * Fires when the button is rendered
27332              * @param {Button} this
27333              */
27334         'render': true
27335     });
27336     if(this.menu){
27337         this.menu = Roo.menu.MenuMgr.get(this.menu);
27338     }
27339     // register listeners first!!  - so render can be captured..
27340     Roo.util.Observable.call(this);
27341     if(renderTo){
27342         this.render(renderTo);
27343     }
27344     
27345   
27346 };
27347
27348 Roo.extend(Roo.Button, Roo.util.Observable, {
27349     /**
27350      * 
27351      */
27352     
27353     /**
27354      * Read-only. True if this button is hidden
27355      * @type Boolean
27356      */
27357     hidden : false,
27358     /**
27359      * Read-only. True if this button is disabled
27360      * @type Boolean
27361      */
27362     disabled : false,
27363     /**
27364      * Read-only. True if this button is pressed (only if enableToggle = true)
27365      * @type Boolean
27366      */
27367     pressed : false,
27368
27369     /**
27370      * @cfg {Number} tabIndex 
27371      * The DOM tabIndex for this button (defaults to undefined)
27372      */
27373     tabIndex : undefined,
27374
27375     /**
27376      * @cfg {Boolean} enableToggle
27377      * True to enable pressed/not pressed toggling (defaults to false)
27378      */
27379     enableToggle: false,
27380     /**
27381      * @cfg {Mixed} menu
27382      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27383      */
27384     menu : undefined,
27385     /**
27386      * @cfg {String} menuAlign
27387      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27388      */
27389     menuAlign : "tl-bl?",
27390
27391     /**
27392      * @cfg {String} iconCls
27393      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27394      */
27395     iconCls : undefined,
27396     /**
27397      * @cfg {String} type
27398      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27399      */
27400     type : 'button',
27401
27402     // private
27403     menuClassTarget: 'tr',
27404
27405     /**
27406      * @cfg {String} clickEvent
27407      * The type of event to map to the button's event handler (defaults to 'click')
27408      */
27409     clickEvent : 'click',
27410
27411     /**
27412      * @cfg {Boolean} handleMouseEvents
27413      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27414      */
27415     handleMouseEvents : true,
27416
27417     /**
27418      * @cfg {String} tooltipType
27419      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27420      */
27421     tooltipType : 'qtip',
27422
27423     /**
27424      * @cfg {String} cls
27425      * A CSS class to apply to the button's main element.
27426      */
27427     
27428     /**
27429      * @cfg {Roo.Template} template (Optional)
27430      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27431      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27432      * require code modifications if required elements (e.g. a button) aren't present.
27433      */
27434
27435     // private
27436     render : function(renderTo){
27437         var btn;
27438         if(this.hideParent){
27439             this.parentEl = Roo.get(renderTo);
27440         }
27441         if(!this.dhconfig){
27442             if(!this.template){
27443                 if(!Roo.Button.buttonTemplate){
27444                     // hideous table template
27445                     Roo.Button.buttonTemplate = new Roo.Template(
27446                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27447                         '<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>',
27448                         "</tr></tbody></table>");
27449                 }
27450                 this.template = Roo.Button.buttonTemplate;
27451             }
27452             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27453             var btnEl = btn.child("button:first");
27454             btnEl.on('focus', this.onFocus, this);
27455             btnEl.on('blur', this.onBlur, this);
27456             if(this.cls){
27457                 btn.addClass(this.cls);
27458             }
27459             if(this.icon){
27460                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27461             }
27462             if(this.iconCls){
27463                 btnEl.addClass(this.iconCls);
27464                 if(!this.cls){
27465                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27466                 }
27467             }
27468             if(this.tabIndex !== undefined){
27469                 btnEl.dom.tabIndex = this.tabIndex;
27470             }
27471             if(this.tooltip){
27472                 if(typeof this.tooltip == 'object'){
27473                     Roo.QuickTips.tips(Roo.apply({
27474                           target: btnEl.id
27475                     }, this.tooltip));
27476                 } else {
27477                     btnEl.dom[this.tooltipType] = this.tooltip;
27478                 }
27479             }
27480         }else{
27481             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27482         }
27483         this.el = btn;
27484         if(this.id){
27485             this.el.dom.id = this.el.id = this.id;
27486         }
27487         if(this.menu){
27488             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27489             this.menu.on("show", this.onMenuShow, this);
27490             this.menu.on("hide", this.onMenuHide, this);
27491         }
27492         btn.addClass("x-btn");
27493         if(Roo.isIE && !Roo.isIE7){
27494             this.autoWidth.defer(1, this);
27495         }else{
27496             this.autoWidth();
27497         }
27498         if(this.handleMouseEvents){
27499             btn.on("mouseover", this.onMouseOver, this);
27500             btn.on("mouseout", this.onMouseOut, this);
27501             btn.on("mousedown", this.onMouseDown, this);
27502         }
27503         btn.on(this.clickEvent, this.onClick, this);
27504         //btn.on("mouseup", this.onMouseUp, this);
27505         if(this.hidden){
27506             this.hide();
27507         }
27508         if(this.disabled){
27509             this.disable();
27510         }
27511         Roo.ButtonToggleMgr.register(this);
27512         if(this.pressed){
27513             this.el.addClass("x-btn-pressed");
27514         }
27515         if(this.repeat){
27516             var repeater = new Roo.util.ClickRepeater(btn,
27517                 typeof this.repeat == "object" ? this.repeat : {}
27518             );
27519             repeater.on("click", this.onClick,  this);
27520         }
27521         
27522         this.fireEvent('render', this);
27523         
27524     },
27525     /**
27526      * Returns the button's underlying element
27527      * @return {Roo.Element} The element
27528      */
27529     getEl : function(){
27530         return this.el;  
27531     },
27532     
27533     /**
27534      * Destroys this Button and removes any listeners.
27535      */
27536     destroy : function(){
27537         Roo.ButtonToggleMgr.unregister(this);
27538         this.el.removeAllListeners();
27539         this.purgeListeners();
27540         this.el.remove();
27541     },
27542
27543     // private
27544     autoWidth : function(){
27545         if(this.el){
27546             this.el.setWidth("auto");
27547             if(Roo.isIE7 && Roo.isStrict){
27548                 var ib = this.el.child('button');
27549                 if(ib && ib.getWidth() > 20){
27550                     ib.clip();
27551                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27552                 }
27553             }
27554             if(this.minWidth){
27555                 if(this.hidden){
27556                     this.el.beginMeasure();
27557                 }
27558                 if(this.el.getWidth() < this.minWidth){
27559                     this.el.setWidth(this.minWidth);
27560                 }
27561                 if(this.hidden){
27562                     this.el.endMeasure();
27563                 }
27564             }
27565         }
27566     },
27567
27568     /**
27569      * Assigns this button's click handler
27570      * @param {Function} handler The function to call when the button is clicked
27571      * @param {Object} scope (optional) Scope for the function passed in
27572      */
27573     setHandler : function(handler, scope){
27574         this.handler = handler;
27575         this.scope = scope;  
27576     },
27577     
27578     /**
27579      * Sets this button's text
27580      * @param {String} text The button text
27581      */
27582     setText : function(text){
27583         this.text = text;
27584         if(this.el){
27585             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27586         }
27587         this.autoWidth();
27588     },
27589     
27590     /**
27591      * Gets the text for this button
27592      * @return {String} The button text
27593      */
27594     getText : function(){
27595         return this.text;  
27596     },
27597     
27598     /**
27599      * Show this button
27600      */
27601     show: function(){
27602         this.hidden = false;
27603         if(this.el){
27604             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27605         }
27606     },
27607     
27608     /**
27609      * Hide this button
27610      */
27611     hide: function(){
27612         this.hidden = true;
27613         if(this.el){
27614             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27615         }
27616     },
27617     
27618     /**
27619      * Convenience function for boolean show/hide
27620      * @param {Boolean} visible True to show, false to hide
27621      */
27622     setVisible: function(visible){
27623         if(visible) {
27624             this.show();
27625         }else{
27626             this.hide();
27627         }
27628     },
27629     
27630     /**
27631      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27632      * @param {Boolean} state (optional) Force a particular state
27633      */
27634     toggle : function(state){
27635         state = state === undefined ? !this.pressed : state;
27636         if(state != this.pressed){
27637             if(state){
27638                 this.el.addClass("x-btn-pressed");
27639                 this.pressed = true;
27640                 this.fireEvent("toggle", this, true);
27641             }else{
27642                 this.el.removeClass("x-btn-pressed");
27643                 this.pressed = false;
27644                 this.fireEvent("toggle", this, false);
27645             }
27646             if(this.toggleHandler){
27647                 this.toggleHandler.call(this.scope || this, this, state);
27648             }
27649         }
27650     },
27651     
27652     /**
27653      * Focus the button
27654      */
27655     focus : function(){
27656         this.el.child('button:first').focus();
27657     },
27658     
27659     /**
27660      * Disable this button
27661      */
27662     disable : function(){
27663         if(this.el){
27664             this.el.addClass("x-btn-disabled");
27665         }
27666         this.disabled = true;
27667     },
27668     
27669     /**
27670      * Enable this button
27671      */
27672     enable : function(){
27673         if(this.el){
27674             this.el.removeClass("x-btn-disabled");
27675         }
27676         this.disabled = false;
27677     },
27678
27679     /**
27680      * Convenience function for boolean enable/disable
27681      * @param {Boolean} enabled True to enable, false to disable
27682      */
27683     setDisabled : function(v){
27684         this[v !== true ? "enable" : "disable"]();
27685     },
27686
27687     // private
27688     onClick : function(e){
27689         if(e){
27690             e.preventDefault();
27691         }
27692         if(e.button != 0){
27693             return;
27694         }
27695         if(!this.disabled){
27696             if(this.enableToggle){
27697                 this.toggle();
27698             }
27699             if(this.menu && !this.menu.isVisible()){
27700                 this.menu.show(this.el, this.menuAlign);
27701             }
27702             this.fireEvent("click", this, e);
27703             if(this.handler){
27704                 this.el.removeClass("x-btn-over");
27705                 this.handler.call(this.scope || this, this, e);
27706             }
27707         }
27708     },
27709     // private
27710     onMouseOver : function(e){
27711         if(!this.disabled){
27712             this.el.addClass("x-btn-over");
27713             this.fireEvent('mouseover', this, e);
27714         }
27715     },
27716     // private
27717     onMouseOut : function(e){
27718         if(!e.within(this.el,  true)){
27719             this.el.removeClass("x-btn-over");
27720             this.fireEvent('mouseout', this, e);
27721         }
27722     },
27723     // private
27724     onFocus : function(e){
27725         if(!this.disabled){
27726             this.el.addClass("x-btn-focus");
27727         }
27728     },
27729     // private
27730     onBlur : function(e){
27731         this.el.removeClass("x-btn-focus");
27732     },
27733     // private
27734     onMouseDown : function(e){
27735         if(!this.disabled && e.button == 0){
27736             this.el.addClass("x-btn-click");
27737             Roo.get(document).on('mouseup', this.onMouseUp, this);
27738         }
27739     },
27740     // private
27741     onMouseUp : function(e){
27742         if(e.button == 0){
27743             this.el.removeClass("x-btn-click");
27744             Roo.get(document).un('mouseup', this.onMouseUp, this);
27745         }
27746     },
27747     // private
27748     onMenuShow : function(e){
27749         this.el.addClass("x-btn-menu-active");
27750     },
27751     // private
27752     onMenuHide : function(e){
27753         this.el.removeClass("x-btn-menu-active");
27754     }   
27755 });
27756
27757 // Private utility class used by Button
27758 Roo.ButtonToggleMgr = function(){
27759    var groups = {};
27760    
27761    function toggleGroup(btn, state){
27762        if(state){
27763            var g = groups[btn.toggleGroup];
27764            for(var i = 0, l = g.length; i < l; i++){
27765                if(g[i] != btn){
27766                    g[i].toggle(false);
27767                }
27768            }
27769        }
27770    }
27771    
27772    return {
27773        register : function(btn){
27774            if(!btn.toggleGroup){
27775                return;
27776            }
27777            var g = groups[btn.toggleGroup];
27778            if(!g){
27779                g = groups[btn.toggleGroup] = [];
27780            }
27781            g.push(btn);
27782            btn.on("toggle", toggleGroup);
27783        },
27784        
27785        unregister : function(btn){
27786            if(!btn.toggleGroup){
27787                return;
27788            }
27789            var g = groups[btn.toggleGroup];
27790            if(g){
27791                g.remove(btn);
27792                btn.un("toggle", toggleGroup);
27793            }
27794        }
27795    };
27796 }();/*
27797  * Based on:
27798  * Ext JS Library 1.1.1
27799  * Copyright(c) 2006-2007, Ext JS, LLC.
27800  *
27801  * Originally Released Under LGPL - original licence link has changed is not relivant.
27802  *
27803  * Fork - LGPL
27804  * <script type="text/javascript">
27805  */
27806  
27807 /**
27808  * @class Roo.SplitButton
27809  * @extends Roo.Button
27810  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27811  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27812  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27813  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27814  * @cfg {String} arrowTooltip The title attribute of the arrow
27815  * @constructor
27816  * Create a new menu button
27817  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27818  * @param {Object} config The config object
27819  */
27820 Roo.SplitButton = function(renderTo, config){
27821     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27822     /**
27823      * @event arrowclick
27824      * Fires when this button's arrow is clicked
27825      * @param {SplitButton} this
27826      * @param {EventObject} e The click event
27827      */
27828     this.addEvents({"arrowclick":true});
27829 };
27830
27831 Roo.extend(Roo.SplitButton, Roo.Button, {
27832     render : function(renderTo){
27833         // this is one sweet looking template!
27834         var tpl = new Roo.Template(
27835             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27836             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27837             '<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>',
27838             "</tbody></table></td><td>",
27839             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27840             '<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>',
27841             "</tbody></table></td></tr></table>"
27842         );
27843         var btn = tpl.append(renderTo, [this.text, this.type], true);
27844         var btnEl = btn.child("button");
27845         if(this.cls){
27846             btn.addClass(this.cls);
27847         }
27848         if(this.icon){
27849             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27850         }
27851         if(this.iconCls){
27852             btnEl.addClass(this.iconCls);
27853             if(!this.cls){
27854                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27855             }
27856         }
27857         this.el = btn;
27858         if(this.handleMouseEvents){
27859             btn.on("mouseover", this.onMouseOver, this);
27860             btn.on("mouseout", this.onMouseOut, this);
27861             btn.on("mousedown", this.onMouseDown, this);
27862             btn.on("mouseup", this.onMouseUp, this);
27863         }
27864         btn.on(this.clickEvent, this.onClick, this);
27865         if(this.tooltip){
27866             if(typeof this.tooltip == 'object'){
27867                 Roo.QuickTips.tips(Roo.apply({
27868                       target: btnEl.id
27869                 }, this.tooltip));
27870             } else {
27871                 btnEl.dom[this.tooltipType] = this.tooltip;
27872             }
27873         }
27874         if(this.arrowTooltip){
27875             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27876         }
27877         if(this.hidden){
27878             this.hide();
27879         }
27880         if(this.disabled){
27881             this.disable();
27882         }
27883         if(this.pressed){
27884             this.el.addClass("x-btn-pressed");
27885         }
27886         if(Roo.isIE && !Roo.isIE7){
27887             this.autoWidth.defer(1, this);
27888         }else{
27889             this.autoWidth();
27890         }
27891         if(this.menu){
27892             this.menu.on("show", this.onMenuShow, this);
27893             this.menu.on("hide", this.onMenuHide, this);
27894         }
27895         this.fireEvent('render', this);
27896     },
27897
27898     // private
27899     autoWidth : function(){
27900         if(this.el){
27901             var tbl = this.el.child("table:first");
27902             var tbl2 = this.el.child("table:last");
27903             this.el.setWidth("auto");
27904             tbl.setWidth("auto");
27905             if(Roo.isIE7 && Roo.isStrict){
27906                 var ib = this.el.child('button:first');
27907                 if(ib && ib.getWidth() > 20){
27908                     ib.clip();
27909                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27910                 }
27911             }
27912             if(this.minWidth){
27913                 if(this.hidden){
27914                     this.el.beginMeasure();
27915                 }
27916                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27917                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27918                 }
27919                 if(this.hidden){
27920                     this.el.endMeasure();
27921                 }
27922             }
27923             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27924         } 
27925     },
27926     /**
27927      * Sets this button's click handler
27928      * @param {Function} handler The function to call when the button is clicked
27929      * @param {Object} scope (optional) Scope for the function passed above
27930      */
27931     setHandler : function(handler, scope){
27932         this.handler = handler;
27933         this.scope = scope;  
27934     },
27935     
27936     /**
27937      * Sets this button's arrow click handler
27938      * @param {Function} handler The function to call when the arrow is clicked
27939      * @param {Object} scope (optional) Scope for the function passed above
27940      */
27941     setArrowHandler : function(handler, scope){
27942         this.arrowHandler = handler;
27943         this.scope = scope;  
27944     },
27945     
27946     /**
27947      * Focus the button
27948      */
27949     focus : function(){
27950         if(this.el){
27951             this.el.child("button:first").focus();
27952         }
27953     },
27954
27955     // private
27956     onClick : function(e){
27957         e.preventDefault();
27958         if(!this.disabled){
27959             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27960                 if(this.menu && !this.menu.isVisible()){
27961                     this.menu.show(this.el, this.menuAlign);
27962                 }
27963                 this.fireEvent("arrowclick", this, e);
27964                 if(this.arrowHandler){
27965                     this.arrowHandler.call(this.scope || this, this, e);
27966                 }
27967             }else{
27968                 this.fireEvent("click", this, e);
27969                 if(this.handler){
27970                     this.handler.call(this.scope || this, this, e);
27971                 }
27972             }
27973         }
27974     },
27975     // private
27976     onMouseDown : function(e){
27977         if(!this.disabled){
27978             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27979         }
27980     },
27981     // private
27982     onMouseUp : function(e){
27983         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27984     }   
27985 });
27986
27987
27988 // backwards compat
27989 Roo.MenuButton = Roo.SplitButton;/*
27990  * Based on:
27991  * Ext JS Library 1.1.1
27992  * Copyright(c) 2006-2007, Ext JS, LLC.
27993  *
27994  * Originally Released Under LGPL - original licence link has changed is not relivant.
27995  *
27996  * Fork - LGPL
27997  * <script type="text/javascript">
27998  */
27999
28000 /**
28001  * @class Roo.Toolbar
28002  * Basic Toolbar class.
28003  * @constructor
28004  * Creates a new Toolbar
28005  * @param {Object} container The config object
28006  */ 
28007 Roo.Toolbar = function(container, buttons, config)
28008 {
28009     /// old consturctor format still supported..
28010     if(container instanceof Array){ // omit the container for later rendering
28011         buttons = container;
28012         config = buttons;
28013         container = null;
28014     }
28015     if (typeof(container) == 'object' && container.xtype) {
28016         config = container;
28017         container = config.container;
28018         buttons = config.buttons || []; // not really - use items!!
28019     }
28020     var xitems = [];
28021     if (config && config.items) {
28022         xitems = config.items;
28023         delete config.items;
28024     }
28025     Roo.apply(this, config);
28026     this.buttons = buttons;
28027     
28028     if(container){
28029         this.render(container);
28030     }
28031     this.xitems = xitems;
28032     Roo.each(xitems, function(b) {
28033         this.add(b);
28034     }, this);
28035     
28036 };
28037
28038 Roo.Toolbar.prototype = {
28039     /**
28040      * @cfg {Array} items
28041      * array of button configs or elements to add (will be converted to a MixedCollection)
28042      */
28043     
28044     /**
28045      * @cfg {String/HTMLElement/Element} container
28046      * The id or element that will contain the toolbar
28047      */
28048     // private
28049     render : function(ct){
28050         this.el = Roo.get(ct);
28051         if(this.cls){
28052             this.el.addClass(this.cls);
28053         }
28054         // using a table allows for vertical alignment
28055         // 100% width is needed by Safari...
28056         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28057         this.tr = this.el.child("tr", true);
28058         var autoId = 0;
28059         this.items = new Roo.util.MixedCollection(false, function(o){
28060             return o.id || ("item" + (++autoId));
28061         });
28062         if(this.buttons){
28063             this.add.apply(this, this.buttons);
28064             delete this.buttons;
28065         }
28066     },
28067
28068     /**
28069      * Adds element(s) to the toolbar -- this function takes a variable number of 
28070      * arguments of mixed type and adds them to the toolbar.
28071      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28072      * <ul>
28073      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28074      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28075      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28076      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28077      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28078      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28079      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28080      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28081      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28082      * </ul>
28083      * @param {Mixed} arg2
28084      * @param {Mixed} etc.
28085      */
28086     add : function(){
28087         var a = arguments, l = a.length;
28088         for(var i = 0; i < l; i++){
28089             this._add(a[i]);
28090         }
28091     },
28092     // private..
28093     _add : function(el) {
28094         
28095         if (el.xtype) {
28096             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28097         }
28098         
28099         if (el.applyTo){ // some kind of form field
28100             return this.addField(el);
28101         } 
28102         if (el.render){ // some kind of Toolbar.Item
28103             return this.addItem(el);
28104         }
28105         if (typeof el == "string"){ // string
28106             if(el == "separator" || el == "-"){
28107                 return this.addSeparator();
28108             }
28109             if (el == " "){
28110                 return this.addSpacer();
28111             }
28112             if(el == "->"){
28113                 return this.addFill();
28114             }
28115             return this.addText(el);
28116             
28117         }
28118         if(el.tagName){ // element
28119             return this.addElement(el);
28120         }
28121         if(typeof el == "object"){ // must be button config?
28122             return this.addButton(el);
28123         }
28124         // and now what?!?!
28125         return false;
28126         
28127     },
28128     
28129     /**
28130      * Add an Xtype element
28131      * @param {Object} xtype Xtype Object
28132      * @return {Object} created Object
28133      */
28134     addxtype : function(e){
28135         return this.add(e);  
28136     },
28137     
28138     /**
28139      * Returns the Element for this toolbar.
28140      * @return {Roo.Element}
28141      */
28142     getEl : function(){
28143         return this.el;  
28144     },
28145     
28146     /**
28147      * Adds a separator
28148      * @return {Roo.Toolbar.Item} The separator item
28149      */
28150     addSeparator : function(){
28151         return this.addItem(new Roo.Toolbar.Separator());
28152     },
28153
28154     /**
28155      * Adds a spacer element
28156      * @return {Roo.Toolbar.Spacer} The spacer item
28157      */
28158     addSpacer : function(){
28159         return this.addItem(new Roo.Toolbar.Spacer());
28160     },
28161
28162     /**
28163      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28164      * @return {Roo.Toolbar.Fill} The fill item
28165      */
28166     addFill : function(){
28167         return this.addItem(new Roo.Toolbar.Fill());
28168     },
28169
28170     /**
28171      * Adds any standard HTML element to the toolbar
28172      * @param {String/HTMLElement/Element} el The element or id of the element to add
28173      * @return {Roo.Toolbar.Item} The element's item
28174      */
28175     addElement : function(el){
28176         return this.addItem(new Roo.Toolbar.Item(el));
28177     },
28178     /**
28179      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28180      * @type Roo.util.MixedCollection  
28181      */
28182     items : false,
28183      
28184     /**
28185      * Adds any Toolbar.Item or subclass
28186      * @param {Roo.Toolbar.Item} item
28187      * @return {Roo.Toolbar.Item} The item
28188      */
28189     addItem : function(item){
28190         var td = this.nextBlock();
28191         item.render(td);
28192         this.items.add(item);
28193         return item;
28194     },
28195     
28196     /**
28197      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28198      * @param {Object/Array} config A button config or array of configs
28199      * @return {Roo.Toolbar.Button/Array}
28200      */
28201     addButton : function(config){
28202         if(config instanceof Array){
28203             var buttons = [];
28204             for(var i = 0, len = config.length; i < len; i++) {
28205                 buttons.push(this.addButton(config[i]));
28206             }
28207             return buttons;
28208         }
28209         var b = config;
28210         if(!(config instanceof Roo.Toolbar.Button)){
28211             b = config.split ?
28212                 new Roo.Toolbar.SplitButton(config) :
28213                 new Roo.Toolbar.Button(config);
28214         }
28215         var td = this.nextBlock();
28216         b.render(td);
28217         this.items.add(b);
28218         return b;
28219     },
28220     
28221     /**
28222      * Adds text to the toolbar
28223      * @param {String} text The text to add
28224      * @return {Roo.Toolbar.Item} The element's item
28225      */
28226     addText : function(text){
28227         return this.addItem(new Roo.Toolbar.TextItem(text));
28228     },
28229     
28230     /**
28231      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28232      * @param {Number} index The index where the item is to be inserted
28233      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28234      * @return {Roo.Toolbar.Button/Item}
28235      */
28236     insertButton : function(index, item){
28237         if(item instanceof Array){
28238             var buttons = [];
28239             for(var i = 0, len = item.length; i < len; i++) {
28240                buttons.push(this.insertButton(index + i, item[i]));
28241             }
28242             return buttons;
28243         }
28244         if (!(item instanceof Roo.Toolbar.Button)){
28245            item = new Roo.Toolbar.Button(item);
28246         }
28247         var td = document.createElement("td");
28248         this.tr.insertBefore(td, this.tr.childNodes[index]);
28249         item.render(td);
28250         this.items.insert(index, item);
28251         return item;
28252     },
28253     
28254     /**
28255      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28256      * @param {Object} config
28257      * @return {Roo.Toolbar.Item} The element's item
28258      */
28259     addDom : function(config, returnEl){
28260         var td = this.nextBlock();
28261         Roo.DomHelper.overwrite(td, config);
28262         var ti = new Roo.Toolbar.Item(td.firstChild);
28263         ti.render(td);
28264         this.items.add(ti);
28265         return ti;
28266     },
28267
28268     /**
28269      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28270      * @type Roo.util.MixedCollection  
28271      */
28272     fields : false,
28273     
28274     /**
28275      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28276      * Note: the field should not have been rendered yet. For a field that has already been
28277      * rendered, use {@link #addElement}.
28278      * @param {Roo.form.Field} field
28279      * @return {Roo.ToolbarItem}
28280      */
28281      
28282       
28283     addField : function(field) {
28284         if (!this.fields) {
28285             var autoId = 0;
28286             this.fields = new Roo.util.MixedCollection(false, function(o){
28287                 return o.id || ("item" + (++autoId));
28288             });
28289
28290         }
28291         
28292         var td = this.nextBlock();
28293         field.render(td);
28294         var ti = new Roo.Toolbar.Item(td.firstChild);
28295         ti.render(td);
28296         this.items.add(ti);
28297         this.fields.add(field);
28298         return ti;
28299     },
28300     /**
28301      * Hide the toolbar
28302      * @method hide
28303      */
28304      
28305       
28306     hide : function()
28307     {
28308         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28309         this.el.child('div').hide();
28310     },
28311     /**
28312      * Show the toolbar
28313      * @method show
28314      */
28315     show : function()
28316     {
28317         this.el.child('div').show();
28318     },
28319       
28320     // private
28321     nextBlock : function(){
28322         var td = document.createElement("td");
28323         this.tr.appendChild(td);
28324         return td;
28325     },
28326
28327     // private
28328     destroy : function(){
28329         if(this.items){ // rendered?
28330             Roo.destroy.apply(Roo, this.items.items);
28331         }
28332         if(this.fields){ // rendered?
28333             Roo.destroy.apply(Roo, this.fields.items);
28334         }
28335         Roo.Element.uncache(this.el, this.tr);
28336     }
28337 };
28338
28339 /**
28340  * @class Roo.Toolbar.Item
28341  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28342  * @constructor
28343  * Creates a new Item
28344  * @param {HTMLElement} el 
28345  */
28346 Roo.Toolbar.Item = function(el){
28347     this.el = Roo.getDom(el);
28348     this.id = Roo.id(this.el);
28349     this.hidden = false;
28350 };
28351
28352 Roo.Toolbar.Item.prototype = {
28353     
28354     /**
28355      * Get this item's HTML Element
28356      * @return {HTMLElement}
28357      */
28358     getEl : function(){
28359        return this.el;  
28360     },
28361
28362     // private
28363     render : function(td){
28364         this.td = td;
28365         td.appendChild(this.el);
28366     },
28367     
28368     /**
28369      * Removes and destroys this item.
28370      */
28371     destroy : function(){
28372         this.td.parentNode.removeChild(this.td);
28373     },
28374     
28375     /**
28376      * Shows this item.
28377      */
28378     show: function(){
28379         this.hidden = false;
28380         this.td.style.display = "";
28381     },
28382     
28383     /**
28384      * Hides this item.
28385      */
28386     hide: function(){
28387         this.hidden = true;
28388         this.td.style.display = "none";
28389     },
28390     
28391     /**
28392      * Convenience function for boolean show/hide.
28393      * @param {Boolean} visible true to show/false to hide
28394      */
28395     setVisible: function(visible){
28396         if(visible) {
28397             this.show();
28398         }else{
28399             this.hide();
28400         }
28401     },
28402     
28403     /**
28404      * Try to focus this item.
28405      */
28406     focus : function(){
28407         Roo.fly(this.el).focus();
28408     },
28409     
28410     /**
28411      * Disables this item.
28412      */
28413     disable : function(){
28414         Roo.fly(this.td).addClass("x-item-disabled");
28415         this.disabled = true;
28416         this.el.disabled = true;
28417     },
28418     
28419     /**
28420      * Enables this item.
28421      */
28422     enable : function(){
28423         Roo.fly(this.td).removeClass("x-item-disabled");
28424         this.disabled = false;
28425         this.el.disabled = false;
28426     }
28427 };
28428
28429
28430 /**
28431  * @class Roo.Toolbar.Separator
28432  * @extends Roo.Toolbar.Item
28433  * A simple toolbar separator class
28434  * @constructor
28435  * Creates a new Separator
28436  */
28437 Roo.Toolbar.Separator = function(){
28438     var s = document.createElement("span");
28439     s.className = "ytb-sep";
28440     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28441 };
28442 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28443     enable:Roo.emptyFn,
28444     disable:Roo.emptyFn,
28445     focus:Roo.emptyFn
28446 });
28447
28448 /**
28449  * @class Roo.Toolbar.Spacer
28450  * @extends Roo.Toolbar.Item
28451  * A simple element that adds extra horizontal space to a toolbar.
28452  * @constructor
28453  * Creates a new Spacer
28454  */
28455 Roo.Toolbar.Spacer = function(){
28456     var s = document.createElement("div");
28457     s.className = "ytb-spacer";
28458     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28459 };
28460 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28461     enable:Roo.emptyFn,
28462     disable:Roo.emptyFn,
28463     focus:Roo.emptyFn
28464 });
28465
28466 /**
28467  * @class Roo.Toolbar.Fill
28468  * @extends Roo.Toolbar.Spacer
28469  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28470  * @constructor
28471  * Creates a new Spacer
28472  */
28473 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28474     // private
28475     render : function(td){
28476         td.style.width = '100%';
28477         Roo.Toolbar.Fill.superclass.render.call(this, td);
28478     }
28479 });
28480
28481 /**
28482  * @class Roo.Toolbar.TextItem
28483  * @extends Roo.Toolbar.Item
28484  * A simple class that renders text directly into a toolbar.
28485  * @constructor
28486  * Creates a new TextItem
28487  * @param {String} text
28488  */
28489 Roo.Toolbar.TextItem = function(text){
28490     if (typeof(text) == 'object') {
28491         text = text.text;
28492     }
28493     var s = document.createElement("span");
28494     s.className = "ytb-text";
28495     s.innerHTML = text;
28496     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28497 };
28498 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28499     enable:Roo.emptyFn,
28500     disable:Roo.emptyFn,
28501     focus:Roo.emptyFn
28502 });
28503
28504 /**
28505  * @class Roo.Toolbar.Button
28506  * @extends Roo.Button
28507  * A button that renders into a toolbar.
28508  * @constructor
28509  * Creates a new Button
28510  * @param {Object} config A standard {@link Roo.Button} config object
28511  */
28512 Roo.Toolbar.Button = function(config){
28513     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28514 };
28515 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28516     render : function(td){
28517         this.td = td;
28518         Roo.Toolbar.Button.superclass.render.call(this, td);
28519     },
28520     
28521     /**
28522      * Removes and destroys this button
28523      */
28524     destroy : function(){
28525         Roo.Toolbar.Button.superclass.destroy.call(this);
28526         this.td.parentNode.removeChild(this.td);
28527     },
28528     
28529     /**
28530      * Shows this button
28531      */
28532     show: function(){
28533         this.hidden = false;
28534         this.td.style.display = "";
28535     },
28536     
28537     /**
28538      * Hides this button
28539      */
28540     hide: function(){
28541         this.hidden = true;
28542         this.td.style.display = "none";
28543     },
28544
28545     /**
28546      * Disables this item
28547      */
28548     disable : function(){
28549         Roo.fly(this.td).addClass("x-item-disabled");
28550         this.disabled = true;
28551     },
28552
28553     /**
28554      * Enables this item
28555      */
28556     enable : function(){
28557         Roo.fly(this.td).removeClass("x-item-disabled");
28558         this.disabled = false;
28559     }
28560 });
28561 // backwards compat
28562 Roo.ToolbarButton = Roo.Toolbar.Button;
28563
28564 /**
28565  * @class Roo.Toolbar.SplitButton
28566  * @extends Roo.SplitButton
28567  * A menu button that renders into a toolbar.
28568  * @constructor
28569  * Creates a new SplitButton
28570  * @param {Object} config A standard {@link Roo.SplitButton} config object
28571  */
28572 Roo.Toolbar.SplitButton = function(config){
28573     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28574 };
28575 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28576     render : function(td){
28577         this.td = td;
28578         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28579     },
28580     
28581     /**
28582      * Removes and destroys this button
28583      */
28584     destroy : function(){
28585         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28586         this.td.parentNode.removeChild(this.td);
28587     },
28588     
28589     /**
28590      * Shows this button
28591      */
28592     show: function(){
28593         this.hidden = false;
28594         this.td.style.display = "";
28595     },
28596     
28597     /**
28598      * Hides this button
28599      */
28600     hide: function(){
28601         this.hidden = true;
28602         this.td.style.display = "none";
28603     }
28604 });
28605
28606 // backwards compat
28607 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28608  * Based on:
28609  * Ext JS Library 1.1.1
28610  * Copyright(c) 2006-2007, Ext JS, LLC.
28611  *
28612  * Originally Released Under LGPL - original licence link has changed is not relivant.
28613  *
28614  * Fork - LGPL
28615  * <script type="text/javascript">
28616  */
28617  
28618 /**
28619  * @class Roo.PagingToolbar
28620  * @extends Roo.Toolbar
28621  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28622  * @constructor
28623  * Create a new PagingToolbar
28624  * @param {Object} config The config object
28625  */
28626 Roo.PagingToolbar = function(el, ds, config)
28627 {
28628     // old args format still supported... - xtype is prefered..
28629     if (typeof(el) == 'object' && el.xtype) {
28630         // created from xtype...
28631         config = el;
28632         ds = el.dataSource;
28633         el = config.container;
28634     }
28635     var items = [];
28636     if (config.items) {
28637         items = config.items;
28638         config.items = [];
28639     }
28640     
28641     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28642     this.ds = ds;
28643     this.cursor = 0;
28644     this.renderButtons(this.el);
28645     this.bind(ds);
28646     
28647     // supprot items array.
28648    
28649     Roo.each(items, function(e) {
28650         this.add(Roo.factory(e));
28651     },this);
28652     
28653 };
28654
28655 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28656     /**
28657      * @cfg {Roo.data.Store} dataSource
28658      * The underlying data store providing the paged data
28659      */
28660     /**
28661      * @cfg {String/HTMLElement/Element} container
28662      * container The id or element that will contain the toolbar
28663      */
28664     /**
28665      * @cfg {Boolean} displayInfo
28666      * True to display the displayMsg (defaults to false)
28667      */
28668     /**
28669      * @cfg {Number} pageSize
28670      * The number of records to display per page (defaults to 20)
28671      */
28672     pageSize: 20,
28673     /**
28674      * @cfg {String} displayMsg
28675      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28676      */
28677     displayMsg : 'Displaying {0} - {1} of {2}',
28678     /**
28679      * @cfg {String} emptyMsg
28680      * The message to display when no records are found (defaults to "No data to display")
28681      */
28682     emptyMsg : 'No data to display',
28683     /**
28684      * Customizable piece of the default paging text (defaults to "Page")
28685      * @type String
28686      */
28687     beforePageText : "Page",
28688     /**
28689      * Customizable piece of the default paging text (defaults to "of %0")
28690      * @type String
28691      */
28692     afterPageText : "of {0}",
28693     /**
28694      * Customizable piece of the default paging text (defaults to "First Page")
28695      * @type String
28696      */
28697     firstText : "First Page",
28698     /**
28699      * Customizable piece of the default paging text (defaults to "Previous Page")
28700      * @type String
28701      */
28702     prevText : "Previous Page",
28703     /**
28704      * Customizable piece of the default paging text (defaults to "Next Page")
28705      * @type String
28706      */
28707     nextText : "Next Page",
28708     /**
28709      * Customizable piece of the default paging text (defaults to "Last Page")
28710      * @type String
28711      */
28712     lastText : "Last Page",
28713     /**
28714      * Customizable piece of the default paging text (defaults to "Refresh")
28715      * @type String
28716      */
28717     refreshText : "Refresh",
28718
28719     // private
28720     renderButtons : function(el){
28721         Roo.PagingToolbar.superclass.render.call(this, el);
28722         this.first = this.addButton({
28723             tooltip: this.firstText,
28724             cls: "x-btn-icon x-grid-page-first",
28725             disabled: true,
28726             handler: this.onClick.createDelegate(this, ["first"])
28727         });
28728         this.prev = this.addButton({
28729             tooltip: this.prevText,
28730             cls: "x-btn-icon x-grid-page-prev",
28731             disabled: true,
28732             handler: this.onClick.createDelegate(this, ["prev"])
28733         });
28734         //this.addSeparator();
28735         this.add(this.beforePageText);
28736         this.field = Roo.get(this.addDom({
28737            tag: "input",
28738            type: "text",
28739            size: "3",
28740            value: "1",
28741            cls: "x-grid-page-number"
28742         }).el);
28743         this.field.on("keydown", this.onPagingKeydown, this);
28744         this.field.on("focus", function(){this.dom.select();});
28745         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28746         this.field.setHeight(18);
28747         //this.addSeparator();
28748         this.next = this.addButton({
28749             tooltip: this.nextText,
28750             cls: "x-btn-icon x-grid-page-next",
28751             disabled: true,
28752             handler: this.onClick.createDelegate(this, ["next"])
28753         });
28754         this.last = this.addButton({
28755             tooltip: this.lastText,
28756             cls: "x-btn-icon x-grid-page-last",
28757             disabled: true,
28758             handler: this.onClick.createDelegate(this, ["last"])
28759         });
28760         //this.addSeparator();
28761         this.loading = this.addButton({
28762             tooltip: this.refreshText,
28763             cls: "x-btn-icon x-grid-loading",
28764             handler: this.onClick.createDelegate(this, ["refresh"])
28765         });
28766
28767         if(this.displayInfo){
28768             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28769         }
28770     },
28771
28772     // private
28773     updateInfo : function(){
28774         if(this.displayEl){
28775             var count = this.ds.getCount();
28776             var msg = count == 0 ?
28777                 this.emptyMsg :
28778                 String.format(
28779                     this.displayMsg,
28780                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28781                 );
28782             this.displayEl.update(msg);
28783         }
28784     },
28785
28786     // private
28787     onLoad : function(ds, r, o){
28788        this.cursor = o.params ? o.params.start : 0;
28789        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28790
28791        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28792        this.field.dom.value = ap;
28793        this.first.setDisabled(ap == 1);
28794        this.prev.setDisabled(ap == 1);
28795        this.next.setDisabled(ap == ps);
28796        this.last.setDisabled(ap == ps);
28797        this.loading.enable();
28798        this.updateInfo();
28799     },
28800
28801     // private
28802     getPageData : function(){
28803         var total = this.ds.getTotalCount();
28804         return {
28805             total : total,
28806             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28807             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28808         };
28809     },
28810
28811     // private
28812     onLoadError : function(){
28813         this.loading.enable();
28814     },
28815
28816     // private
28817     onPagingKeydown : function(e){
28818         var k = e.getKey();
28819         var d = this.getPageData();
28820         if(k == e.RETURN){
28821             var v = this.field.dom.value, pageNum;
28822             if(!v || isNaN(pageNum = parseInt(v, 10))){
28823                 this.field.dom.value = d.activePage;
28824                 return;
28825             }
28826             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28827             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28828             e.stopEvent();
28829         }
28830         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))
28831         {
28832           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28833           this.field.dom.value = pageNum;
28834           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28835           e.stopEvent();
28836         }
28837         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28838         {
28839           var v = this.field.dom.value, pageNum; 
28840           var increment = (e.shiftKey) ? 10 : 1;
28841           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28842             increment *= -1;
28843           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28844             this.field.dom.value = d.activePage;
28845             return;
28846           }
28847           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28848           {
28849             this.field.dom.value = parseInt(v, 10) + increment;
28850             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28851             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28852           }
28853           e.stopEvent();
28854         }
28855     },
28856
28857     // private
28858     beforeLoad : function(){
28859         if(this.loading){
28860             this.loading.disable();
28861         }
28862     },
28863
28864     // private
28865     onClick : function(which){
28866         var ds = this.ds;
28867         switch(which){
28868             case "first":
28869                 ds.load({params:{start: 0, limit: this.pageSize}});
28870             break;
28871             case "prev":
28872                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28873             break;
28874             case "next":
28875                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28876             break;
28877             case "last":
28878                 var total = ds.getTotalCount();
28879                 var extra = total % this.pageSize;
28880                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28881                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28882             break;
28883             case "refresh":
28884                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28885             break;
28886         }
28887     },
28888
28889     /**
28890      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28891      * @param {Roo.data.Store} store The data store to unbind
28892      */
28893     unbind : function(ds){
28894         ds.un("beforeload", this.beforeLoad, this);
28895         ds.un("load", this.onLoad, this);
28896         ds.un("loadexception", this.onLoadError, this);
28897         ds.un("remove", this.updateInfo, this);
28898         ds.un("add", this.updateInfo, this);
28899         this.ds = undefined;
28900     },
28901
28902     /**
28903      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28904      * @param {Roo.data.Store} store The data store to bind
28905      */
28906     bind : function(ds){
28907         ds.on("beforeload", this.beforeLoad, this);
28908         ds.on("load", this.onLoad, this);
28909         ds.on("loadexception", this.onLoadError, this);
28910         ds.on("remove", this.updateInfo, this);
28911         ds.on("add", this.updateInfo, this);
28912         this.ds = ds;
28913     }
28914 });/*
28915  * Based on:
28916  * Ext JS Library 1.1.1
28917  * Copyright(c) 2006-2007, Ext JS, LLC.
28918  *
28919  * Originally Released Under LGPL - original licence link has changed is not relivant.
28920  *
28921  * Fork - LGPL
28922  * <script type="text/javascript">
28923  */
28924
28925 /**
28926  * @class Roo.Resizable
28927  * @extends Roo.util.Observable
28928  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28929  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28930  * 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
28931  * the element will be wrapped for you automatically.</p>
28932  * <p>Here is the list of valid resize handles:</p>
28933  * <pre>
28934 Value   Description
28935 ------  -------------------
28936  'n'     north
28937  's'     south
28938  'e'     east
28939  'w'     west
28940  'nw'    northwest
28941  'sw'    southwest
28942  'se'    southeast
28943  'ne'    northeast
28944  'hd'    horizontal drag
28945  'all'   all
28946 </pre>
28947  * <p>Here's an example showing the creation of a typical Resizable:</p>
28948  * <pre><code>
28949 var resizer = new Roo.Resizable("element-id", {
28950     handles: 'all',
28951     minWidth: 200,
28952     minHeight: 100,
28953     maxWidth: 500,
28954     maxHeight: 400,
28955     pinned: true
28956 });
28957 resizer.on("resize", myHandler);
28958 </code></pre>
28959  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28960  * resizer.east.setDisplayed(false);</p>
28961  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28962  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28963  * resize operation's new size (defaults to [0, 0])
28964  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28965  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28966  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28967  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28968  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28969  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28970  * @cfg {Number} width The width of the element in pixels (defaults to null)
28971  * @cfg {Number} height The height of the element in pixels (defaults to null)
28972  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28973  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28974  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28975  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28976  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28977  * in favor of the handles config option (defaults to false)
28978  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28979  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28980  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28981  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28982  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28983  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28984  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28985  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28986  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28987  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28988  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28989  * @constructor
28990  * Create a new resizable component
28991  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28992  * @param {Object} config configuration options
28993   */
28994 Roo.Resizable = function(el, config)
28995 {
28996     this.el = Roo.get(el);
28997
28998     if(config && config.wrap){
28999         config.resizeChild = this.el;
29000         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29001         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29002         this.el.setStyle("overflow", "hidden");
29003         this.el.setPositioning(config.resizeChild.getPositioning());
29004         config.resizeChild.clearPositioning();
29005         if(!config.width || !config.height){
29006             var csize = config.resizeChild.getSize();
29007             this.el.setSize(csize.width, csize.height);
29008         }
29009         if(config.pinned && !config.adjustments){
29010             config.adjustments = "auto";
29011         }
29012     }
29013
29014     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29015     this.proxy.unselectable();
29016     this.proxy.enableDisplayMode('block');
29017
29018     Roo.apply(this, config);
29019
29020     if(this.pinned){
29021         this.disableTrackOver = true;
29022         this.el.addClass("x-resizable-pinned");
29023     }
29024     // if the element isn't positioned, make it relative
29025     var position = this.el.getStyle("position");
29026     if(position != "absolute" && position != "fixed"){
29027         this.el.setStyle("position", "relative");
29028     }
29029     if(!this.handles){ // no handles passed, must be legacy style
29030         this.handles = 's,e,se';
29031         if(this.multiDirectional){
29032             this.handles += ',n,w';
29033         }
29034     }
29035     if(this.handles == "all"){
29036         this.handles = "n s e w ne nw se sw";
29037     }
29038     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29039     var ps = Roo.Resizable.positions;
29040     for(var i = 0, len = hs.length; i < len; i++){
29041         if(hs[i] && ps[hs[i]]){
29042             var pos = ps[hs[i]];
29043             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29044         }
29045     }
29046     // legacy
29047     this.corner = this.southeast;
29048     
29049     // updateBox = the box can move..
29050     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29051         this.updateBox = true;
29052     }
29053
29054     this.activeHandle = null;
29055
29056     if(this.resizeChild){
29057         if(typeof this.resizeChild == "boolean"){
29058             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29059         }else{
29060             this.resizeChild = Roo.get(this.resizeChild, true);
29061         }
29062     }
29063     
29064     if(this.adjustments == "auto"){
29065         var rc = this.resizeChild;
29066         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29067         if(rc && (hw || hn)){
29068             rc.position("relative");
29069             rc.setLeft(hw ? hw.el.getWidth() : 0);
29070             rc.setTop(hn ? hn.el.getHeight() : 0);
29071         }
29072         this.adjustments = [
29073             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29074             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29075         ];
29076     }
29077
29078     if(this.draggable){
29079         this.dd = this.dynamic ?
29080             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29081         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29082     }
29083
29084     // public events
29085     this.addEvents({
29086         /**
29087          * @event beforeresize
29088          * Fired before resize is allowed. Set enabled to false to cancel resize.
29089          * @param {Roo.Resizable} this
29090          * @param {Roo.EventObject} e The mousedown event
29091          */
29092         "beforeresize" : true,
29093         /**
29094          * @event resizing
29095          * Fired a resizing.
29096          * @param {Roo.Resizable} this
29097          * @param {Number} x The new x position
29098          * @param {Number} y The new y position
29099          * @param {Number} w The new w width
29100          * @param {Number} h The new h hight
29101          * @param {Roo.EventObject} e The mouseup event
29102          */
29103         "resizing" : true,
29104         /**
29105          * @event resize
29106          * Fired after a resize.
29107          * @param {Roo.Resizable} this
29108          * @param {Number} width The new width
29109          * @param {Number} height The new height
29110          * @param {Roo.EventObject} e The mouseup event
29111          */
29112         "resize" : true
29113     });
29114
29115     if(this.width !== null && this.height !== null){
29116         this.resizeTo(this.width, this.height);
29117     }else{
29118         this.updateChildSize();
29119     }
29120     if(Roo.isIE){
29121         this.el.dom.style.zoom = 1;
29122     }
29123     Roo.Resizable.superclass.constructor.call(this);
29124 };
29125
29126 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29127         resizeChild : false,
29128         adjustments : [0, 0],
29129         minWidth : 5,
29130         minHeight : 5,
29131         maxWidth : 10000,
29132         maxHeight : 10000,
29133         enabled : true,
29134         animate : false,
29135         duration : .35,
29136         dynamic : false,
29137         handles : false,
29138         multiDirectional : false,
29139         disableTrackOver : false,
29140         easing : 'easeOutStrong',
29141         widthIncrement : 0,
29142         heightIncrement : 0,
29143         pinned : false,
29144         width : null,
29145         height : null,
29146         preserveRatio : false,
29147         transparent: false,
29148         minX: 0,
29149         minY: 0,
29150         draggable: false,
29151
29152         /**
29153          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29154          */
29155         constrainTo: undefined,
29156         /**
29157          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29158          */
29159         resizeRegion: undefined,
29160
29161
29162     /**
29163      * Perform a manual resize
29164      * @param {Number} width
29165      * @param {Number} height
29166      */
29167     resizeTo : function(width, height){
29168         this.el.setSize(width, height);
29169         this.updateChildSize();
29170         this.fireEvent("resize", this, width, height, null);
29171     },
29172
29173     // private
29174     startSizing : function(e, handle){
29175         this.fireEvent("beforeresize", this, e);
29176         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29177
29178             if(!this.overlay){
29179                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29180                 this.overlay.unselectable();
29181                 this.overlay.enableDisplayMode("block");
29182                 this.overlay.on("mousemove", this.onMouseMove, this);
29183                 this.overlay.on("mouseup", this.onMouseUp, this);
29184             }
29185             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29186
29187             this.resizing = true;
29188             this.startBox = this.el.getBox();
29189             this.startPoint = e.getXY();
29190             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29191                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29192
29193             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29194             this.overlay.show();
29195
29196             if(this.constrainTo) {
29197                 var ct = Roo.get(this.constrainTo);
29198                 this.resizeRegion = ct.getRegion().adjust(
29199                     ct.getFrameWidth('t'),
29200                     ct.getFrameWidth('l'),
29201                     -ct.getFrameWidth('b'),
29202                     -ct.getFrameWidth('r')
29203                 );
29204             }
29205
29206             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29207             this.proxy.show();
29208             this.proxy.setBox(this.startBox);
29209             if(!this.dynamic){
29210                 this.proxy.setStyle('visibility', 'visible');
29211             }
29212         }
29213     },
29214
29215     // private
29216     onMouseDown : function(handle, e){
29217         if(this.enabled){
29218             e.stopEvent();
29219             this.activeHandle = handle;
29220             this.startSizing(e, handle);
29221         }
29222     },
29223
29224     // private
29225     onMouseUp : function(e){
29226         var size = this.resizeElement();
29227         this.resizing = false;
29228         this.handleOut();
29229         this.overlay.hide();
29230         this.proxy.hide();
29231         this.fireEvent("resize", this, size.width, size.height, e);
29232     },
29233
29234     // private
29235     updateChildSize : function(){
29236         
29237         if(this.resizeChild){
29238             var el = this.el;
29239             var child = this.resizeChild;
29240             var adj = this.adjustments;
29241             if(el.dom.offsetWidth){
29242                 var b = el.getSize(true);
29243                 child.setSize(b.width+adj[0], b.height+adj[1]);
29244             }
29245             // Second call here for IE
29246             // The first call enables instant resizing and
29247             // the second call corrects scroll bars if they
29248             // exist
29249             if(Roo.isIE){
29250                 setTimeout(function(){
29251                     if(el.dom.offsetWidth){
29252                         var b = el.getSize(true);
29253                         child.setSize(b.width+adj[0], b.height+adj[1]);
29254                     }
29255                 }, 10);
29256             }
29257         }
29258     },
29259
29260     // private
29261     snap : function(value, inc, min){
29262         if(!inc || !value) return value;
29263         var newValue = value;
29264         var m = value % inc;
29265         if(m > 0){
29266             if(m > (inc/2)){
29267                 newValue = value + (inc-m);
29268             }else{
29269                 newValue = value - m;
29270             }
29271         }
29272         return Math.max(min, newValue);
29273     },
29274
29275     // private
29276     resizeElement : function(){
29277         var box = this.proxy.getBox();
29278         if(this.updateBox){
29279             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29280         }else{
29281             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29282         }
29283         this.updateChildSize();
29284         if(!this.dynamic){
29285             this.proxy.hide();
29286         }
29287         return box;
29288     },
29289
29290     // private
29291     constrain : function(v, diff, m, mx){
29292         if(v - diff < m){
29293             diff = v - m;
29294         }else if(v - diff > mx){
29295             diff = mx - v;
29296         }
29297         return diff;
29298     },
29299
29300     // private
29301     onMouseMove : function(e){
29302         
29303         if(this.enabled){
29304             try{// try catch so if something goes wrong the user doesn't get hung
29305
29306             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29307                 return;
29308             }
29309
29310             //var curXY = this.startPoint;
29311             var curSize = this.curSize || this.startBox;
29312             var x = this.startBox.x, y = this.startBox.y;
29313             var ox = x, oy = y;
29314             var w = curSize.width, h = curSize.height;
29315             var ow = w, oh = h;
29316             var mw = this.minWidth, mh = this.minHeight;
29317             var mxw = this.maxWidth, mxh = this.maxHeight;
29318             var wi = this.widthIncrement;
29319             var hi = this.heightIncrement;
29320
29321             var eventXY = e.getXY();
29322             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29323             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29324
29325             var pos = this.activeHandle.position;
29326
29327             switch(pos){
29328                 case "east":
29329                     w += diffX;
29330                     w = Math.min(Math.max(mw, w), mxw);
29331                     break;
29332              
29333                 case "south":
29334                     h += diffY;
29335                     h = Math.min(Math.max(mh, h), mxh);
29336                     break;
29337                 case "southeast":
29338                     w += diffX;
29339                     h += diffY;
29340                     w = Math.min(Math.max(mw, w), mxw);
29341                     h = Math.min(Math.max(mh, h), mxh);
29342                     break;
29343                 case "north":
29344                     diffY = this.constrain(h, diffY, mh, mxh);
29345                     y += diffY;
29346                     h -= diffY;
29347                     break;
29348                 case "hdrag":
29349                     
29350                     if (wi) {
29351                         var adiffX = Math.abs(diffX);
29352                         var sub = (adiffX % wi); // how much 
29353                         if (sub > (wi/2)) { // far enough to snap
29354                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29355                         } else {
29356                             // remove difference.. 
29357                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29358                         }
29359                     }
29360                     x += diffX;
29361                     x = Math.max(this.minX, x);
29362                     break;
29363                 case "west":
29364                     diffX = this.constrain(w, diffX, mw, mxw);
29365                     x += diffX;
29366                     w -= diffX;
29367                     break;
29368                 case "northeast":
29369                     w += diffX;
29370                     w = Math.min(Math.max(mw, w), mxw);
29371                     diffY = this.constrain(h, diffY, mh, mxh);
29372                     y += diffY;
29373                     h -= diffY;
29374                     break;
29375                 case "northwest":
29376                     diffX = this.constrain(w, diffX, mw, mxw);
29377                     diffY = this.constrain(h, diffY, mh, mxh);
29378                     y += diffY;
29379                     h -= diffY;
29380                     x += diffX;
29381                     w -= diffX;
29382                     break;
29383                case "southwest":
29384                     diffX = this.constrain(w, diffX, mw, mxw);
29385                     h += diffY;
29386                     h = Math.min(Math.max(mh, h), mxh);
29387                     x += diffX;
29388                     w -= diffX;
29389                     break;
29390             }
29391
29392             var sw = this.snap(w, wi, mw);
29393             var sh = this.snap(h, hi, mh);
29394             if(sw != w || sh != h){
29395                 switch(pos){
29396                     case "northeast":
29397                         y -= sh - h;
29398                     break;
29399                     case "north":
29400                         y -= sh - h;
29401                         break;
29402                     case "southwest":
29403                         x -= sw - w;
29404                     break;
29405                     case "west":
29406                         x -= sw - w;
29407                         break;
29408                     case "northwest":
29409                         x -= sw - w;
29410                         y -= sh - h;
29411                     break;
29412                 }
29413                 w = sw;
29414                 h = sh;
29415             }
29416
29417             if(this.preserveRatio){
29418                 switch(pos){
29419                     case "southeast":
29420                     case "east":
29421                         h = oh * (w/ow);
29422                         h = Math.min(Math.max(mh, h), mxh);
29423                         w = ow * (h/oh);
29424                        break;
29425                     case "south":
29426                         w = ow * (h/oh);
29427                         w = Math.min(Math.max(mw, w), mxw);
29428                         h = oh * (w/ow);
29429                         break;
29430                     case "northeast":
29431                         w = ow * (h/oh);
29432                         w = Math.min(Math.max(mw, w), mxw);
29433                         h = oh * (w/ow);
29434                     break;
29435                     case "north":
29436                         var tw = w;
29437                         w = ow * (h/oh);
29438                         w = Math.min(Math.max(mw, w), mxw);
29439                         h = oh * (w/ow);
29440                         x += (tw - w) / 2;
29441                         break;
29442                     case "southwest":
29443                         h = oh * (w/ow);
29444                         h = Math.min(Math.max(mh, h), mxh);
29445                         var tw = w;
29446                         w = ow * (h/oh);
29447                         x += tw - w;
29448                         break;
29449                     case "west":
29450                         var th = h;
29451                         h = oh * (w/ow);
29452                         h = Math.min(Math.max(mh, h), mxh);
29453                         y += (th - h) / 2;
29454                         var tw = w;
29455                         w = ow * (h/oh);
29456                         x += tw - w;
29457                        break;
29458                     case "northwest":
29459                         var tw = w;
29460                         var th = h;
29461                         h = oh * (w/ow);
29462                         h = Math.min(Math.max(mh, h), mxh);
29463                         w = ow * (h/oh);
29464                         y += th - h;
29465                         x += tw - w;
29466                        break;
29467
29468                 }
29469             }
29470             if (pos == 'hdrag') {
29471                 w = ow;
29472             }
29473             this.proxy.setBounds(x, y, w, h);
29474             if(this.dynamic){
29475                 this.resizeElement();
29476             }
29477             }catch(e){}
29478         }
29479         this.fireEvent("resizing", this, x, y, w, h, e);
29480     },
29481
29482     // private
29483     handleOver : function(){
29484         if(this.enabled){
29485             this.el.addClass("x-resizable-over");
29486         }
29487     },
29488
29489     // private
29490     handleOut : function(){
29491         if(!this.resizing){
29492             this.el.removeClass("x-resizable-over");
29493         }
29494     },
29495
29496     /**
29497      * Returns the element this component is bound to.
29498      * @return {Roo.Element}
29499      */
29500     getEl : function(){
29501         return this.el;
29502     },
29503
29504     /**
29505      * Returns the resizeChild element (or null).
29506      * @return {Roo.Element}
29507      */
29508     getResizeChild : function(){
29509         return this.resizeChild;
29510     },
29511     groupHandler : function()
29512     {
29513         
29514     },
29515     /**
29516      * Destroys this resizable. If the element was wrapped and
29517      * removeEl is not true then the element remains.
29518      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29519      */
29520     destroy : function(removeEl){
29521         this.proxy.remove();
29522         if(this.overlay){
29523             this.overlay.removeAllListeners();
29524             this.overlay.remove();
29525         }
29526         var ps = Roo.Resizable.positions;
29527         for(var k in ps){
29528             if(typeof ps[k] != "function" && this[ps[k]]){
29529                 var h = this[ps[k]];
29530                 h.el.removeAllListeners();
29531                 h.el.remove();
29532             }
29533         }
29534         if(removeEl){
29535             this.el.update("");
29536             this.el.remove();
29537         }
29538     }
29539 });
29540
29541 // private
29542 // hash to map config positions to true positions
29543 Roo.Resizable.positions = {
29544     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29545     hd: "hdrag"
29546 };
29547
29548 // private
29549 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29550     if(!this.tpl){
29551         // only initialize the template if resizable is used
29552         var tpl = Roo.DomHelper.createTemplate(
29553             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29554         );
29555         tpl.compile();
29556         Roo.Resizable.Handle.prototype.tpl = tpl;
29557     }
29558     this.position = pos;
29559     this.rz = rz;
29560     // show north drag fro topdra
29561     var handlepos = pos == 'hdrag' ? 'north' : pos;
29562     
29563     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29564     if (pos == 'hdrag') {
29565         this.el.setStyle('cursor', 'pointer');
29566     }
29567     this.el.unselectable();
29568     if(transparent){
29569         this.el.setOpacity(0);
29570     }
29571     this.el.on("mousedown", this.onMouseDown, this);
29572     if(!disableTrackOver){
29573         this.el.on("mouseover", this.onMouseOver, this);
29574         this.el.on("mouseout", this.onMouseOut, this);
29575     }
29576 };
29577
29578 // private
29579 Roo.Resizable.Handle.prototype = {
29580     afterResize : function(rz){
29581         Roo.log('after?');
29582         // do nothing
29583     },
29584     // private
29585     onMouseDown : function(e){
29586         this.rz.onMouseDown(this, e);
29587     },
29588     // private
29589     onMouseOver : function(e){
29590         this.rz.handleOver(this, e);
29591     },
29592     // private
29593     onMouseOut : function(e){
29594         this.rz.handleOut(this, e);
29595     }
29596 };/*
29597  * Based on:
29598  * Ext JS Library 1.1.1
29599  * Copyright(c) 2006-2007, Ext JS, LLC.
29600  *
29601  * Originally Released Under LGPL - original licence link has changed is not relivant.
29602  *
29603  * Fork - LGPL
29604  * <script type="text/javascript">
29605  */
29606
29607 /**
29608  * @class Roo.Editor
29609  * @extends Roo.Component
29610  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29611  * @constructor
29612  * Create a new Editor
29613  * @param {Roo.form.Field} field The Field object (or descendant)
29614  * @param {Object} config The config object
29615  */
29616 Roo.Editor = function(field, config){
29617     Roo.Editor.superclass.constructor.call(this, config);
29618     this.field = field;
29619     this.addEvents({
29620         /**
29621              * @event beforestartedit
29622              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29623              * false from the handler of this event.
29624              * @param {Editor} this
29625              * @param {Roo.Element} boundEl The underlying element bound to this editor
29626              * @param {Mixed} value The field value being set
29627              */
29628         "beforestartedit" : true,
29629         /**
29630              * @event startedit
29631              * Fires when this editor is displayed
29632              * @param {Roo.Element} boundEl The underlying element bound to this editor
29633              * @param {Mixed} value The starting field value
29634              */
29635         "startedit" : true,
29636         /**
29637              * @event beforecomplete
29638              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29639              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29640              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29641              * event will not fire since no edit actually occurred.
29642              * @param {Editor} this
29643              * @param {Mixed} value The current field value
29644              * @param {Mixed} startValue The original field value
29645              */
29646         "beforecomplete" : true,
29647         /**
29648              * @event complete
29649              * Fires after editing is complete and any changed value has been written to the underlying field.
29650              * @param {Editor} this
29651              * @param {Mixed} value The current field value
29652              * @param {Mixed} startValue The original field value
29653              */
29654         "complete" : true,
29655         /**
29656          * @event specialkey
29657          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29658          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29659          * @param {Roo.form.Field} this
29660          * @param {Roo.EventObject} e The event object
29661          */
29662         "specialkey" : true
29663     });
29664 };
29665
29666 Roo.extend(Roo.Editor, Roo.Component, {
29667     /**
29668      * @cfg {Boolean/String} autosize
29669      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29670      * or "height" to adopt the height only (defaults to false)
29671      */
29672     /**
29673      * @cfg {Boolean} revertInvalid
29674      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29675      * validation fails (defaults to true)
29676      */
29677     /**
29678      * @cfg {Boolean} ignoreNoChange
29679      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29680      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29681      * will never be ignored.
29682      */
29683     /**
29684      * @cfg {Boolean} hideEl
29685      * False to keep the bound element visible while the editor is displayed (defaults to true)
29686      */
29687     /**
29688      * @cfg {Mixed} value
29689      * The data value of the underlying field (defaults to "")
29690      */
29691     value : "",
29692     /**
29693      * @cfg {String} alignment
29694      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29695      */
29696     alignment: "c-c?",
29697     /**
29698      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29699      * for bottom-right shadow (defaults to "frame")
29700      */
29701     shadow : "frame",
29702     /**
29703      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29704      */
29705     constrain : false,
29706     /**
29707      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29708      */
29709     completeOnEnter : false,
29710     /**
29711      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29712      */
29713     cancelOnEsc : false,
29714     /**
29715      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29716      */
29717     updateEl : false,
29718
29719     // private
29720     onRender : function(ct, position){
29721         this.el = new Roo.Layer({
29722             shadow: this.shadow,
29723             cls: "x-editor",
29724             parentEl : ct,
29725             shim : this.shim,
29726             shadowOffset:4,
29727             id: this.id,
29728             constrain: this.constrain
29729         });
29730         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29731         if(this.field.msgTarget != 'title'){
29732             this.field.msgTarget = 'qtip';
29733         }
29734         this.field.render(this.el);
29735         if(Roo.isGecko){
29736             this.field.el.dom.setAttribute('autocomplete', 'off');
29737         }
29738         this.field.on("specialkey", this.onSpecialKey, this);
29739         if(this.swallowKeys){
29740             this.field.el.swallowEvent(['keydown','keypress']);
29741         }
29742         this.field.show();
29743         this.field.on("blur", this.onBlur, this);
29744         if(this.field.grow){
29745             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29746         }
29747     },
29748
29749     onSpecialKey : function(field, e)
29750     {
29751         //Roo.log('editor onSpecialKey');
29752         if(this.completeOnEnter && e.getKey() == e.ENTER){
29753             e.stopEvent();
29754             this.completeEdit();
29755             return;
29756         }
29757         // do not fire special key otherwise it might hide close the editor...
29758         if(e.getKey() == e.ENTER){    
29759             return;
29760         }
29761         if(this.cancelOnEsc && e.getKey() == e.ESC){
29762             this.cancelEdit();
29763             return;
29764         } 
29765         this.fireEvent('specialkey', field, e);
29766     
29767     },
29768
29769     /**
29770      * Starts the editing process and shows the editor.
29771      * @param {String/HTMLElement/Element} el The element to edit
29772      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29773       * to the innerHTML of el.
29774      */
29775     startEdit : function(el, value){
29776         if(this.editing){
29777             this.completeEdit();
29778         }
29779         this.boundEl = Roo.get(el);
29780         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29781         if(!this.rendered){
29782             this.render(this.parentEl || document.body);
29783         }
29784         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29785             return;
29786         }
29787         this.startValue = v;
29788         this.field.setValue(v);
29789         if(this.autoSize){
29790             var sz = this.boundEl.getSize();
29791             switch(this.autoSize){
29792                 case "width":
29793                 this.setSize(sz.width,  "");
29794                 break;
29795                 case "height":
29796                 this.setSize("",  sz.height);
29797                 break;
29798                 default:
29799                 this.setSize(sz.width,  sz.height);
29800             }
29801         }
29802         this.el.alignTo(this.boundEl, this.alignment);
29803         this.editing = true;
29804         if(Roo.QuickTips){
29805             Roo.QuickTips.disable();
29806         }
29807         this.show();
29808     },
29809
29810     /**
29811      * Sets the height and width of this editor.
29812      * @param {Number} width The new width
29813      * @param {Number} height The new height
29814      */
29815     setSize : function(w, h){
29816         this.field.setSize(w, h);
29817         if(this.el){
29818             this.el.sync();
29819         }
29820     },
29821
29822     /**
29823      * Realigns the editor to the bound field based on the current alignment config value.
29824      */
29825     realign : function(){
29826         this.el.alignTo(this.boundEl, this.alignment);
29827     },
29828
29829     /**
29830      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29831      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29832      */
29833     completeEdit : function(remainVisible){
29834         if(!this.editing){
29835             return;
29836         }
29837         var v = this.getValue();
29838         if(this.revertInvalid !== false && !this.field.isValid()){
29839             v = this.startValue;
29840             this.cancelEdit(true);
29841         }
29842         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29843             this.editing = false;
29844             this.hide();
29845             return;
29846         }
29847         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29848             this.editing = false;
29849             if(this.updateEl && this.boundEl){
29850                 this.boundEl.update(v);
29851             }
29852             if(remainVisible !== true){
29853                 this.hide();
29854             }
29855             this.fireEvent("complete", this, v, this.startValue);
29856         }
29857     },
29858
29859     // private
29860     onShow : function(){
29861         this.el.show();
29862         if(this.hideEl !== false){
29863             this.boundEl.hide();
29864         }
29865         this.field.show();
29866         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29867             this.fixIEFocus = true;
29868             this.deferredFocus.defer(50, this);
29869         }else{
29870             this.field.focus();
29871         }
29872         this.fireEvent("startedit", this.boundEl, this.startValue);
29873     },
29874
29875     deferredFocus : function(){
29876         if(this.editing){
29877             this.field.focus();
29878         }
29879     },
29880
29881     /**
29882      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29883      * reverted to the original starting value.
29884      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29885      * cancel (defaults to false)
29886      */
29887     cancelEdit : function(remainVisible){
29888         if(this.editing){
29889             this.setValue(this.startValue);
29890             if(remainVisible !== true){
29891                 this.hide();
29892             }
29893         }
29894     },
29895
29896     // private
29897     onBlur : function(){
29898         if(this.allowBlur !== true && this.editing){
29899             this.completeEdit();
29900         }
29901     },
29902
29903     // private
29904     onHide : function(){
29905         if(this.editing){
29906             this.completeEdit();
29907             return;
29908         }
29909         this.field.blur();
29910         if(this.field.collapse){
29911             this.field.collapse();
29912         }
29913         this.el.hide();
29914         if(this.hideEl !== false){
29915             this.boundEl.show();
29916         }
29917         if(Roo.QuickTips){
29918             Roo.QuickTips.enable();
29919         }
29920     },
29921
29922     /**
29923      * Sets the data value of the editor
29924      * @param {Mixed} value Any valid value supported by the underlying field
29925      */
29926     setValue : function(v){
29927         this.field.setValue(v);
29928     },
29929
29930     /**
29931      * Gets the data value of the editor
29932      * @return {Mixed} The data value
29933      */
29934     getValue : function(){
29935         return this.field.getValue();
29936     }
29937 });/*
29938  * Based on:
29939  * Ext JS Library 1.1.1
29940  * Copyright(c) 2006-2007, Ext JS, LLC.
29941  *
29942  * Originally Released Under LGPL - original licence link has changed is not relivant.
29943  *
29944  * Fork - LGPL
29945  * <script type="text/javascript">
29946  */
29947  
29948 /**
29949  * @class Roo.BasicDialog
29950  * @extends Roo.util.Observable
29951  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29952  * <pre><code>
29953 var dlg = new Roo.BasicDialog("my-dlg", {
29954     height: 200,
29955     width: 300,
29956     minHeight: 100,
29957     minWidth: 150,
29958     modal: true,
29959     proxyDrag: true,
29960     shadow: true
29961 });
29962 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29963 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29964 dlg.addButton('Cancel', dlg.hide, dlg);
29965 dlg.show();
29966 </code></pre>
29967   <b>A Dialog should always be a direct child of the body element.</b>
29968  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29969  * @cfg {String} title Default text to display in the title bar (defaults to null)
29970  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29971  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29972  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29973  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29974  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29975  * (defaults to null with no animation)
29976  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29977  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29978  * property for valid values (defaults to 'all')
29979  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29980  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29981  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29982  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29983  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29984  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29985  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29986  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29987  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29988  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29989  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29990  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29991  * draggable = true (defaults to false)
29992  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29993  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29994  * shadow (defaults to false)
29995  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29996  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29997  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29998  * @cfg {Array} buttons Array of buttons
29999  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30000  * @constructor
30001  * Create a new BasicDialog.
30002  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30003  * @param {Object} config Configuration options
30004  */
30005 Roo.BasicDialog = function(el, config){
30006     this.el = Roo.get(el);
30007     var dh = Roo.DomHelper;
30008     if(!this.el && config && config.autoCreate){
30009         if(typeof config.autoCreate == "object"){
30010             if(!config.autoCreate.id){
30011                 config.autoCreate.id = el;
30012             }
30013             this.el = dh.append(document.body,
30014                         config.autoCreate, true);
30015         }else{
30016             this.el = dh.append(document.body,
30017                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30018         }
30019     }
30020     el = this.el;
30021     el.setDisplayed(true);
30022     el.hide = this.hideAction;
30023     this.id = el.id;
30024     el.addClass("x-dlg");
30025
30026     Roo.apply(this, config);
30027
30028     this.proxy = el.createProxy("x-dlg-proxy");
30029     this.proxy.hide = this.hideAction;
30030     this.proxy.setOpacity(.5);
30031     this.proxy.hide();
30032
30033     if(config.width){
30034         el.setWidth(config.width);
30035     }
30036     if(config.height){
30037         el.setHeight(config.height);
30038     }
30039     this.size = el.getSize();
30040     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30041         this.xy = [config.x,config.y];
30042     }else{
30043         this.xy = el.getCenterXY(true);
30044     }
30045     /** The header element @type Roo.Element */
30046     this.header = el.child("> .x-dlg-hd");
30047     /** The body element @type Roo.Element */
30048     this.body = el.child("> .x-dlg-bd");
30049     /** The footer element @type Roo.Element */
30050     this.footer = el.child("> .x-dlg-ft");
30051
30052     if(!this.header){
30053         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30054     }
30055     if(!this.body){
30056         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30057     }
30058
30059     this.header.unselectable();
30060     if(this.title){
30061         this.header.update(this.title);
30062     }
30063     // this element allows the dialog to be focused for keyboard event
30064     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30065     this.focusEl.swallowEvent("click", true);
30066
30067     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30068
30069     // wrap the body and footer for special rendering
30070     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30071     if(this.footer){
30072         this.bwrap.dom.appendChild(this.footer.dom);
30073     }
30074
30075     this.bg = this.el.createChild({
30076         tag: "div", cls:"x-dlg-bg",
30077         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30078     });
30079     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30080
30081
30082     if(this.autoScroll !== false && !this.autoTabs){
30083         this.body.setStyle("overflow", "auto");
30084     }
30085
30086     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30087
30088     if(this.closable !== false){
30089         this.el.addClass("x-dlg-closable");
30090         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30091         this.close.on("click", this.closeClick, this);
30092         this.close.addClassOnOver("x-dlg-close-over");
30093     }
30094     if(this.collapsible !== false){
30095         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30096         this.collapseBtn.on("click", this.collapseClick, this);
30097         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30098         this.header.on("dblclick", this.collapseClick, this);
30099     }
30100     if(this.resizable !== false){
30101         this.el.addClass("x-dlg-resizable");
30102         this.resizer = new Roo.Resizable(el, {
30103             minWidth: this.minWidth || 80,
30104             minHeight:this.minHeight || 80,
30105             handles: this.resizeHandles || "all",
30106             pinned: true
30107         });
30108         this.resizer.on("beforeresize", this.beforeResize, this);
30109         this.resizer.on("resize", this.onResize, this);
30110     }
30111     if(this.draggable !== false){
30112         el.addClass("x-dlg-draggable");
30113         if (!this.proxyDrag) {
30114             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30115         }
30116         else {
30117             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30118         }
30119         dd.setHandleElId(this.header.id);
30120         dd.endDrag = this.endMove.createDelegate(this);
30121         dd.startDrag = this.startMove.createDelegate(this);
30122         dd.onDrag = this.onDrag.createDelegate(this);
30123         dd.scroll = false;
30124         this.dd = dd;
30125     }
30126     if(this.modal){
30127         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30128         this.mask.enableDisplayMode("block");
30129         this.mask.hide();
30130         this.el.addClass("x-dlg-modal");
30131     }
30132     if(this.shadow){
30133         this.shadow = new Roo.Shadow({
30134             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30135             offset : this.shadowOffset
30136         });
30137     }else{
30138         this.shadowOffset = 0;
30139     }
30140     if(Roo.useShims && this.shim !== false){
30141         this.shim = this.el.createShim();
30142         this.shim.hide = this.hideAction;
30143         this.shim.hide();
30144     }else{
30145         this.shim = false;
30146     }
30147     if(this.autoTabs){
30148         this.initTabs();
30149     }
30150     if (this.buttons) { 
30151         var bts= this.buttons;
30152         this.buttons = [];
30153         Roo.each(bts, function(b) {
30154             this.addButton(b);
30155         }, this);
30156     }
30157     
30158     
30159     this.addEvents({
30160         /**
30161          * @event keydown
30162          * Fires when a key is pressed
30163          * @param {Roo.BasicDialog} this
30164          * @param {Roo.EventObject} e
30165          */
30166         "keydown" : true,
30167         /**
30168          * @event move
30169          * Fires when this dialog is moved by the user.
30170          * @param {Roo.BasicDialog} this
30171          * @param {Number} x The new page X
30172          * @param {Number} y The new page Y
30173          */
30174         "move" : true,
30175         /**
30176          * @event resize
30177          * Fires when this dialog is resized by the user.
30178          * @param {Roo.BasicDialog} this
30179          * @param {Number} width The new width
30180          * @param {Number} height The new height
30181          */
30182         "resize" : true,
30183         /**
30184          * @event beforehide
30185          * Fires before this dialog is hidden.
30186          * @param {Roo.BasicDialog} this
30187          */
30188         "beforehide" : true,
30189         /**
30190          * @event hide
30191          * Fires when this dialog is hidden.
30192          * @param {Roo.BasicDialog} this
30193          */
30194         "hide" : true,
30195         /**
30196          * @event beforeshow
30197          * Fires before this dialog is shown.
30198          * @param {Roo.BasicDialog} this
30199          */
30200         "beforeshow" : true,
30201         /**
30202          * @event show
30203          * Fires when this dialog is shown.
30204          * @param {Roo.BasicDialog} this
30205          */
30206         "show" : true
30207     });
30208     el.on("keydown", this.onKeyDown, this);
30209     el.on("mousedown", this.toFront, this);
30210     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30211     this.el.hide();
30212     Roo.DialogManager.register(this);
30213     Roo.BasicDialog.superclass.constructor.call(this);
30214 };
30215
30216 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30217     shadowOffset: Roo.isIE ? 6 : 5,
30218     minHeight: 80,
30219     minWidth: 200,
30220     minButtonWidth: 75,
30221     defaultButton: null,
30222     buttonAlign: "right",
30223     tabTag: 'div',
30224     firstShow: true,
30225
30226     /**
30227      * Sets the dialog title text
30228      * @param {String} text The title text to display
30229      * @return {Roo.BasicDialog} this
30230      */
30231     setTitle : function(text){
30232         this.header.update(text);
30233         return this;
30234     },
30235
30236     // private
30237     closeClick : function(){
30238         this.hide();
30239     },
30240
30241     // private
30242     collapseClick : function(){
30243         this[this.collapsed ? "expand" : "collapse"]();
30244     },
30245
30246     /**
30247      * Collapses the dialog to its minimized state (only the title bar is visible).
30248      * Equivalent to the user clicking the collapse dialog button.
30249      */
30250     collapse : function(){
30251         if(!this.collapsed){
30252             this.collapsed = true;
30253             this.el.addClass("x-dlg-collapsed");
30254             this.restoreHeight = this.el.getHeight();
30255             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30256         }
30257     },
30258
30259     /**
30260      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30261      * clicking the expand dialog button.
30262      */
30263     expand : function(){
30264         if(this.collapsed){
30265             this.collapsed = false;
30266             this.el.removeClass("x-dlg-collapsed");
30267             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30268         }
30269     },
30270
30271     /**
30272      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30273      * @return {Roo.TabPanel} The tabs component
30274      */
30275     initTabs : function(){
30276         var tabs = this.getTabs();
30277         while(tabs.getTab(0)){
30278             tabs.removeTab(0);
30279         }
30280         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30281             var dom = el.dom;
30282             tabs.addTab(Roo.id(dom), dom.title);
30283             dom.title = "";
30284         });
30285         tabs.activate(0);
30286         return tabs;
30287     },
30288
30289     // private
30290     beforeResize : function(){
30291         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30292     },
30293
30294     // private
30295     onResize : function(){
30296         this.refreshSize();
30297         this.syncBodyHeight();
30298         this.adjustAssets();
30299         this.focus();
30300         this.fireEvent("resize", this, this.size.width, this.size.height);
30301     },
30302
30303     // private
30304     onKeyDown : function(e){
30305         if(this.isVisible()){
30306             this.fireEvent("keydown", this, e);
30307         }
30308     },
30309
30310     /**
30311      * Resizes the dialog.
30312      * @param {Number} width
30313      * @param {Number} height
30314      * @return {Roo.BasicDialog} this
30315      */
30316     resizeTo : function(width, height){
30317         this.el.setSize(width, height);
30318         this.size = {width: width, height: height};
30319         this.syncBodyHeight();
30320         if(this.fixedcenter){
30321             this.center();
30322         }
30323         if(this.isVisible()){
30324             this.constrainXY();
30325             this.adjustAssets();
30326         }
30327         this.fireEvent("resize", this, width, height);
30328         return this;
30329     },
30330
30331
30332     /**
30333      * Resizes the dialog to fit the specified content size.
30334      * @param {Number} width
30335      * @param {Number} height
30336      * @return {Roo.BasicDialog} this
30337      */
30338     setContentSize : function(w, h){
30339         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30340         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30341         //if(!this.el.isBorderBox()){
30342             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30343             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30344         //}
30345         if(this.tabs){
30346             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30347             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30348         }
30349         this.resizeTo(w, h);
30350         return this;
30351     },
30352
30353     /**
30354      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30355      * executed in response to a particular key being pressed while the dialog is active.
30356      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30357      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30358      * @param {Function} fn The function to call
30359      * @param {Object} scope (optional) The scope of the function
30360      * @return {Roo.BasicDialog} this
30361      */
30362     addKeyListener : function(key, fn, scope){
30363         var keyCode, shift, ctrl, alt;
30364         if(typeof key == "object" && !(key instanceof Array)){
30365             keyCode = key["key"];
30366             shift = key["shift"];
30367             ctrl = key["ctrl"];
30368             alt = key["alt"];
30369         }else{
30370             keyCode = key;
30371         }
30372         var handler = function(dlg, e){
30373             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30374                 var k = e.getKey();
30375                 if(keyCode instanceof Array){
30376                     for(var i = 0, len = keyCode.length; i < len; i++){
30377                         if(keyCode[i] == k){
30378                           fn.call(scope || window, dlg, k, e);
30379                           return;
30380                         }
30381                     }
30382                 }else{
30383                     if(k == keyCode){
30384                         fn.call(scope || window, dlg, k, e);
30385                     }
30386                 }
30387             }
30388         };
30389         this.on("keydown", handler);
30390         return this;
30391     },
30392
30393     /**
30394      * Returns the TabPanel component (creates it if it doesn't exist).
30395      * Note: If you wish to simply check for the existence of tabs without creating them,
30396      * check for a null 'tabs' property.
30397      * @return {Roo.TabPanel} The tabs component
30398      */
30399     getTabs : function(){
30400         if(!this.tabs){
30401             this.el.addClass("x-dlg-auto-tabs");
30402             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30403             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30404         }
30405         return this.tabs;
30406     },
30407
30408     /**
30409      * Adds a button to the footer section of the dialog.
30410      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30411      * object or a valid Roo.DomHelper element config
30412      * @param {Function} handler The function called when the button is clicked
30413      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30414      * @return {Roo.Button} The new button
30415      */
30416     addButton : function(config, handler, scope){
30417         var dh = Roo.DomHelper;
30418         if(!this.footer){
30419             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30420         }
30421         if(!this.btnContainer){
30422             var tb = this.footer.createChild({
30423
30424                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30425                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30426             }, null, true);
30427             this.btnContainer = tb.firstChild.firstChild.firstChild;
30428         }
30429         var bconfig = {
30430             handler: handler,
30431             scope: scope,
30432             minWidth: this.minButtonWidth,
30433             hideParent:true
30434         };
30435         if(typeof config == "string"){
30436             bconfig.text = config;
30437         }else{
30438             if(config.tag){
30439                 bconfig.dhconfig = config;
30440             }else{
30441                 Roo.apply(bconfig, config);
30442             }
30443         }
30444         var fc = false;
30445         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30446             bconfig.position = Math.max(0, bconfig.position);
30447             fc = this.btnContainer.childNodes[bconfig.position];
30448         }
30449          
30450         var btn = new Roo.Button(
30451             fc ? 
30452                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30453                 : this.btnContainer.appendChild(document.createElement("td")),
30454             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30455             bconfig
30456         );
30457         this.syncBodyHeight();
30458         if(!this.buttons){
30459             /**
30460              * Array of all the buttons that have been added to this dialog via addButton
30461              * @type Array
30462              */
30463             this.buttons = [];
30464         }
30465         this.buttons.push(btn);
30466         return btn;
30467     },
30468
30469     /**
30470      * Sets the default button to be focused when the dialog is displayed.
30471      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30472      * @return {Roo.BasicDialog} this
30473      */
30474     setDefaultButton : function(btn){
30475         this.defaultButton = btn;
30476         return this;
30477     },
30478
30479     // private
30480     getHeaderFooterHeight : function(safe){
30481         var height = 0;
30482         if(this.header){
30483            height += this.header.getHeight();
30484         }
30485         if(this.footer){
30486            var fm = this.footer.getMargins();
30487             height += (this.footer.getHeight()+fm.top+fm.bottom);
30488         }
30489         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30490         height += this.centerBg.getPadding("tb");
30491         return height;
30492     },
30493
30494     // private
30495     syncBodyHeight : function()
30496     {
30497         var bd = this.body, // the text
30498             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30499             bw = this.bwrap;
30500         var height = this.size.height - this.getHeaderFooterHeight(false);
30501         bd.setHeight(height-bd.getMargins("tb"));
30502         var hh = this.header.getHeight();
30503         var h = this.size.height-hh;
30504         cb.setHeight(h);
30505         
30506         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30507         bw.setHeight(h-cb.getPadding("tb"));
30508         
30509         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30510         bd.setWidth(bw.getWidth(true));
30511         if(this.tabs){
30512             this.tabs.syncHeight();
30513             if(Roo.isIE){
30514                 this.tabs.el.repaint();
30515             }
30516         }
30517     },
30518
30519     /**
30520      * Restores the previous state of the dialog if Roo.state is configured.
30521      * @return {Roo.BasicDialog} this
30522      */
30523     restoreState : function(){
30524         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30525         if(box && box.width){
30526             this.xy = [box.x, box.y];
30527             this.resizeTo(box.width, box.height);
30528         }
30529         return this;
30530     },
30531
30532     // private
30533     beforeShow : function(){
30534         this.expand();
30535         if(this.fixedcenter){
30536             this.xy = this.el.getCenterXY(true);
30537         }
30538         if(this.modal){
30539             Roo.get(document.body).addClass("x-body-masked");
30540             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30541             this.mask.show();
30542         }
30543         this.constrainXY();
30544     },
30545
30546     // private
30547     animShow : function(){
30548         var b = Roo.get(this.animateTarget).getBox();
30549         this.proxy.setSize(b.width, b.height);
30550         this.proxy.setLocation(b.x, b.y);
30551         this.proxy.show();
30552         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30553                     true, .35, this.showEl.createDelegate(this));
30554     },
30555
30556     /**
30557      * Shows the dialog.
30558      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30559      * @return {Roo.BasicDialog} this
30560      */
30561     show : function(animateTarget){
30562         if (this.fireEvent("beforeshow", this) === false){
30563             return;
30564         }
30565         if(this.syncHeightBeforeShow){
30566             this.syncBodyHeight();
30567         }else if(this.firstShow){
30568             this.firstShow = false;
30569             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30570         }
30571         this.animateTarget = animateTarget || this.animateTarget;
30572         if(!this.el.isVisible()){
30573             this.beforeShow();
30574             if(this.animateTarget && Roo.get(this.animateTarget)){
30575                 this.animShow();
30576             }else{
30577                 this.showEl();
30578             }
30579         }
30580         return this;
30581     },
30582
30583     // private
30584     showEl : function(){
30585         this.proxy.hide();
30586         this.el.setXY(this.xy);
30587         this.el.show();
30588         this.adjustAssets(true);
30589         this.toFront();
30590         this.focus();
30591         // IE peekaboo bug - fix found by Dave Fenwick
30592         if(Roo.isIE){
30593             this.el.repaint();
30594         }
30595         this.fireEvent("show", this);
30596     },
30597
30598     /**
30599      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30600      * dialog itself will receive focus.
30601      */
30602     focus : function(){
30603         if(this.defaultButton){
30604             this.defaultButton.focus();
30605         }else{
30606             this.focusEl.focus();
30607         }
30608     },
30609
30610     // private
30611     constrainXY : function(){
30612         if(this.constraintoviewport !== false){
30613             if(!this.viewSize){
30614                 if(this.container){
30615                     var s = this.container.getSize();
30616                     this.viewSize = [s.width, s.height];
30617                 }else{
30618                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30619                 }
30620             }
30621             var s = Roo.get(this.container||document).getScroll();
30622
30623             var x = this.xy[0], y = this.xy[1];
30624             var w = this.size.width, h = this.size.height;
30625             var vw = this.viewSize[0], vh = this.viewSize[1];
30626             // only move it if it needs it
30627             var moved = false;
30628             // first validate right/bottom
30629             if(x + w > vw+s.left){
30630                 x = vw - w;
30631                 moved = true;
30632             }
30633             if(y + h > vh+s.top){
30634                 y = vh - h;
30635                 moved = true;
30636             }
30637             // then make sure top/left isn't negative
30638             if(x < s.left){
30639                 x = s.left;
30640                 moved = true;
30641             }
30642             if(y < s.top){
30643                 y = s.top;
30644                 moved = true;
30645             }
30646             if(moved){
30647                 // cache xy
30648                 this.xy = [x, y];
30649                 if(this.isVisible()){
30650                     this.el.setLocation(x, y);
30651                     this.adjustAssets();
30652                 }
30653             }
30654         }
30655     },
30656
30657     // private
30658     onDrag : function(){
30659         if(!this.proxyDrag){
30660             this.xy = this.el.getXY();
30661             this.adjustAssets();
30662         }
30663     },
30664
30665     // private
30666     adjustAssets : function(doShow){
30667         var x = this.xy[0], y = this.xy[1];
30668         var w = this.size.width, h = this.size.height;
30669         if(doShow === true){
30670             if(this.shadow){
30671                 this.shadow.show(this.el);
30672             }
30673             if(this.shim){
30674                 this.shim.show();
30675             }
30676         }
30677         if(this.shadow && this.shadow.isVisible()){
30678             this.shadow.show(this.el);
30679         }
30680         if(this.shim && this.shim.isVisible()){
30681             this.shim.setBounds(x, y, w, h);
30682         }
30683     },
30684
30685     // private
30686     adjustViewport : function(w, h){
30687         if(!w || !h){
30688             w = Roo.lib.Dom.getViewWidth();
30689             h = Roo.lib.Dom.getViewHeight();
30690         }
30691         // cache the size
30692         this.viewSize = [w, h];
30693         if(this.modal && this.mask.isVisible()){
30694             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30695             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30696         }
30697         if(this.isVisible()){
30698             this.constrainXY();
30699         }
30700     },
30701
30702     /**
30703      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30704      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30705      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30706      */
30707     destroy : function(removeEl){
30708         if(this.isVisible()){
30709             this.animateTarget = null;
30710             this.hide();
30711         }
30712         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30713         if(this.tabs){
30714             this.tabs.destroy(removeEl);
30715         }
30716         Roo.destroy(
30717              this.shim,
30718              this.proxy,
30719              this.resizer,
30720              this.close,
30721              this.mask
30722         );
30723         if(this.dd){
30724             this.dd.unreg();
30725         }
30726         if(this.buttons){
30727            for(var i = 0, len = this.buttons.length; i < len; i++){
30728                this.buttons[i].destroy();
30729            }
30730         }
30731         this.el.removeAllListeners();
30732         if(removeEl === true){
30733             this.el.update("");
30734             this.el.remove();
30735         }
30736         Roo.DialogManager.unregister(this);
30737     },
30738
30739     // private
30740     startMove : function(){
30741         if(this.proxyDrag){
30742             this.proxy.show();
30743         }
30744         if(this.constraintoviewport !== false){
30745             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30746         }
30747     },
30748
30749     // private
30750     endMove : function(){
30751         if(!this.proxyDrag){
30752             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30753         }else{
30754             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30755             this.proxy.hide();
30756         }
30757         this.refreshSize();
30758         this.adjustAssets();
30759         this.focus();
30760         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30761     },
30762
30763     /**
30764      * Brings this dialog to the front of any other visible dialogs
30765      * @return {Roo.BasicDialog} this
30766      */
30767     toFront : function(){
30768         Roo.DialogManager.bringToFront(this);
30769         return this;
30770     },
30771
30772     /**
30773      * Sends this dialog to the back (under) of any other visible dialogs
30774      * @return {Roo.BasicDialog} this
30775      */
30776     toBack : function(){
30777         Roo.DialogManager.sendToBack(this);
30778         return this;
30779     },
30780
30781     /**
30782      * Centers this dialog in the viewport
30783      * @return {Roo.BasicDialog} this
30784      */
30785     center : function(){
30786         var xy = this.el.getCenterXY(true);
30787         this.moveTo(xy[0], xy[1]);
30788         return this;
30789     },
30790
30791     /**
30792      * Moves the dialog's top-left corner to the specified point
30793      * @param {Number} x
30794      * @param {Number} y
30795      * @return {Roo.BasicDialog} this
30796      */
30797     moveTo : function(x, y){
30798         this.xy = [x,y];
30799         if(this.isVisible()){
30800             this.el.setXY(this.xy);
30801             this.adjustAssets();
30802         }
30803         return this;
30804     },
30805
30806     /**
30807      * Aligns the dialog to the specified element
30808      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30809      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30810      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30811      * @return {Roo.BasicDialog} this
30812      */
30813     alignTo : function(element, position, offsets){
30814         this.xy = this.el.getAlignToXY(element, position, offsets);
30815         if(this.isVisible()){
30816             this.el.setXY(this.xy);
30817             this.adjustAssets();
30818         }
30819         return this;
30820     },
30821
30822     /**
30823      * Anchors an element to another element and realigns it when the window is resized.
30824      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30825      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30826      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30827      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30828      * is a number, it is used as the buffer delay (defaults to 50ms).
30829      * @return {Roo.BasicDialog} this
30830      */
30831     anchorTo : function(el, alignment, offsets, monitorScroll){
30832         var action = function(){
30833             this.alignTo(el, alignment, offsets);
30834         };
30835         Roo.EventManager.onWindowResize(action, this);
30836         var tm = typeof monitorScroll;
30837         if(tm != 'undefined'){
30838             Roo.EventManager.on(window, 'scroll', action, this,
30839                 {buffer: tm == 'number' ? monitorScroll : 50});
30840         }
30841         action.call(this);
30842         return this;
30843     },
30844
30845     /**
30846      * Returns true if the dialog is visible
30847      * @return {Boolean}
30848      */
30849     isVisible : function(){
30850         return this.el.isVisible();
30851     },
30852
30853     // private
30854     animHide : function(callback){
30855         var b = Roo.get(this.animateTarget).getBox();
30856         this.proxy.show();
30857         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30858         this.el.hide();
30859         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30860                     this.hideEl.createDelegate(this, [callback]));
30861     },
30862
30863     /**
30864      * Hides the dialog.
30865      * @param {Function} callback (optional) Function to call when the dialog is hidden
30866      * @return {Roo.BasicDialog} this
30867      */
30868     hide : function(callback){
30869         if (this.fireEvent("beforehide", this) === false){
30870             return;
30871         }
30872         if(this.shadow){
30873             this.shadow.hide();
30874         }
30875         if(this.shim) {
30876           this.shim.hide();
30877         }
30878         // sometimes animateTarget seems to get set.. causing problems...
30879         // this just double checks..
30880         if(this.animateTarget && Roo.get(this.animateTarget)) {
30881            this.animHide(callback);
30882         }else{
30883             this.el.hide();
30884             this.hideEl(callback);
30885         }
30886         return this;
30887     },
30888
30889     // private
30890     hideEl : function(callback){
30891         this.proxy.hide();
30892         if(this.modal){
30893             this.mask.hide();
30894             Roo.get(document.body).removeClass("x-body-masked");
30895         }
30896         this.fireEvent("hide", this);
30897         if(typeof callback == "function"){
30898             callback();
30899         }
30900     },
30901
30902     // private
30903     hideAction : function(){
30904         this.setLeft("-10000px");
30905         this.setTop("-10000px");
30906         this.setStyle("visibility", "hidden");
30907     },
30908
30909     // private
30910     refreshSize : function(){
30911         this.size = this.el.getSize();
30912         this.xy = this.el.getXY();
30913         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30914     },
30915
30916     // private
30917     // z-index is managed by the DialogManager and may be overwritten at any time
30918     setZIndex : function(index){
30919         if(this.modal){
30920             this.mask.setStyle("z-index", index);
30921         }
30922         if(this.shim){
30923             this.shim.setStyle("z-index", ++index);
30924         }
30925         if(this.shadow){
30926             this.shadow.setZIndex(++index);
30927         }
30928         this.el.setStyle("z-index", ++index);
30929         if(this.proxy){
30930             this.proxy.setStyle("z-index", ++index);
30931         }
30932         if(this.resizer){
30933             this.resizer.proxy.setStyle("z-index", ++index);
30934         }
30935
30936         this.lastZIndex = index;
30937     },
30938
30939     /**
30940      * Returns the element for this dialog
30941      * @return {Roo.Element} The underlying dialog Element
30942      */
30943     getEl : function(){
30944         return this.el;
30945     }
30946 });
30947
30948 /**
30949  * @class Roo.DialogManager
30950  * Provides global access to BasicDialogs that have been created and
30951  * support for z-indexing (layering) multiple open dialogs.
30952  */
30953 Roo.DialogManager = function(){
30954     var list = {};
30955     var accessList = [];
30956     var front = null;
30957
30958     // private
30959     var sortDialogs = function(d1, d2){
30960         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30961     };
30962
30963     // private
30964     var orderDialogs = function(){
30965         accessList.sort(sortDialogs);
30966         var seed = Roo.DialogManager.zseed;
30967         for(var i = 0, len = accessList.length; i < len; i++){
30968             var dlg = accessList[i];
30969             if(dlg){
30970                 dlg.setZIndex(seed + (i*10));
30971             }
30972         }
30973     };
30974
30975     return {
30976         /**
30977          * The starting z-index for BasicDialogs (defaults to 9000)
30978          * @type Number The z-index value
30979          */
30980         zseed : 9000,
30981
30982         // private
30983         register : function(dlg){
30984             list[dlg.id] = dlg;
30985             accessList.push(dlg);
30986         },
30987
30988         // private
30989         unregister : function(dlg){
30990             delete list[dlg.id];
30991             var i=0;
30992             var len=0;
30993             if(!accessList.indexOf){
30994                 for(  i = 0, len = accessList.length; i < len; i++){
30995                     if(accessList[i] == dlg){
30996                         accessList.splice(i, 1);
30997                         return;
30998                     }
30999                 }
31000             }else{
31001                  i = accessList.indexOf(dlg);
31002                 if(i != -1){
31003                     accessList.splice(i, 1);
31004                 }
31005             }
31006         },
31007
31008         /**
31009          * Gets a registered dialog by id
31010          * @param {String/Object} id The id of the dialog or a dialog
31011          * @return {Roo.BasicDialog} this
31012          */
31013         get : function(id){
31014             return typeof id == "object" ? id : list[id];
31015         },
31016
31017         /**
31018          * Brings the specified dialog to the front
31019          * @param {String/Object} dlg The id of the dialog or a dialog
31020          * @return {Roo.BasicDialog} this
31021          */
31022         bringToFront : function(dlg){
31023             dlg = this.get(dlg);
31024             if(dlg != front){
31025                 front = dlg;
31026                 dlg._lastAccess = new Date().getTime();
31027                 orderDialogs();
31028             }
31029             return dlg;
31030         },
31031
31032         /**
31033          * Sends the specified dialog to the back
31034          * @param {String/Object} dlg The id of the dialog or a dialog
31035          * @return {Roo.BasicDialog} this
31036          */
31037         sendToBack : function(dlg){
31038             dlg = this.get(dlg);
31039             dlg._lastAccess = -(new Date().getTime());
31040             orderDialogs();
31041             return dlg;
31042         },
31043
31044         /**
31045          * Hides all dialogs
31046          */
31047         hideAll : function(){
31048             for(var id in list){
31049                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31050                     list[id].hide();
31051                 }
31052             }
31053         }
31054     };
31055 }();
31056
31057 /**
31058  * @class Roo.LayoutDialog
31059  * @extends Roo.BasicDialog
31060  * Dialog which provides adjustments for working with a layout in a Dialog.
31061  * Add your necessary layout config options to the dialog's config.<br>
31062  * Example usage (including a nested layout):
31063  * <pre><code>
31064 if(!dialog){
31065     dialog = new Roo.LayoutDialog("download-dlg", {
31066         modal: true,
31067         width:600,
31068         height:450,
31069         shadow:true,
31070         minWidth:500,
31071         minHeight:350,
31072         autoTabs:true,
31073         proxyDrag:true,
31074         // layout config merges with the dialog config
31075         center:{
31076             tabPosition: "top",
31077             alwaysShowTabs: true
31078         }
31079     });
31080     dialog.addKeyListener(27, dialog.hide, dialog);
31081     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31082     dialog.addButton("Build It!", this.getDownload, this);
31083
31084     // we can even add nested layouts
31085     var innerLayout = new Roo.BorderLayout("dl-inner", {
31086         east: {
31087             initialSize: 200,
31088             autoScroll:true,
31089             split:true
31090         },
31091         center: {
31092             autoScroll:true
31093         }
31094     });
31095     innerLayout.beginUpdate();
31096     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31097     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31098     innerLayout.endUpdate(true);
31099
31100     var layout = dialog.getLayout();
31101     layout.beginUpdate();
31102     layout.add("center", new Roo.ContentPanel("standard-panel",
31103                         {title: "Download the Source", fitToFrame:true}));
31104     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31105                {title: "Build your own roo.js"}));
31106     layout.getRegion("center").showPanel(sp);
31107     layout.endUpdate();
31108 }
31109 </code></pre>
31110     * @constructor
31111     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31112     * @param {Object} config configuration options
31113   */
31114 Roo.LayoutDialog = function(el, cfg){
31115     
31116     var config=  cfg;
31117     if (typeof(cfg) == 'undefined') {
31118         config = Roo.apply({}, el);
31119         // not sure why we use documentElement here.. - it should always be body.
31120         // IE7 borks horribly if we use documentElement.
31121         // webkit also does not like documentElement - it creates a body element...
31122         el = Roo.get( document.body || document.documentElement ).createChild();
31123         //config.autoCreate = true;
31124     }
31125     
31126     
31127     config.autoTabs = false;
31128     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31129     this.body.setStyle({overflow:"hidden", position:"relative"});
31130     this.layout = new Roo.BorderLayout(this.body.dom, config);
31131     this.layout.monitorWindowResize = false;
31132     this.el.addClass("x-dlg-auto-layout");
31133     // fix case when center region overwrites center function
31134     this.center = Roo.BasicDialog.prototype.center;
31135     this.on("show", this.layout.layout, this.layout, true);
31136     if (config.items) {
31137         var xitems = config.items;
31138         delete config.items;
31139         Roo.each(xitems, this.addxtype, this);
31140     }
31141     
31142     
31143 };
31144 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31145     /**
31146      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31147      * @deprecated
31148      */
31149     endUpdate : function(){
31150         this.layout.endUpdate();
31151     },
31152
31153     /**
31154      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31155      *  @deprecated
31156      */
31157     beginUpdate : function(){
31158         this.layout.beginUpdate();
31159     },
31160
31161     /**
31162      * Get the BorderLayout for this dialog
31163      * @return {Roo.BorderLayout}
31164      */
31165     getLayout : function(){
31166         return this.layout;
31167     },
31168
31169     showEl : function(){
31170         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31171         if(Roo.isIE7){
31172             this.layout.layout();
31173         }
31174     },
31175
31176     // private
31177     // Use the syncHeightBeforeShow config option to control this automatically
31178     syncBodyHeight : function(){
31179         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31180         if(this.layout){this.layout.layout();}
31181     },
31182     
31183       /**
31184      * Add an xtype element (actually adds to the layout.)
31185      * @return {Object} xdata xtype object data.
31186      */
31187     
31188     addxtype : function(c) {
31189         return this.layout.addxtype(c);
31190     }
31191 });/*
31192  * Based on:
31193  * Ext JS Library 1.1.1
31194  * Copyright(c) 2006-2007, Ext JS, LLC.
31195  *
31196  * Originally Released Under LGPL - original licence link has changed is not relivant.
31197  *
31198  * Fork - LGPL
31199  * <script type="text/javascript">
31200  */
31201  
31202 /**
31203  * @class Roo.MessageBox
31204  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31205  * Example usage:
31206  *<pre><code>
31207 // Basic alert:
31208 Roo.Msg.alert('Status', 'Changes saved successfully.');
31209
31210 // Prompt for user data:
31211 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31212     if (btn == 'ok'){
31213         // process text value...
31214     }
31215 });
31216
31217 // Show a dialog using config options:
31218 Roo.Msg.show({
31219    title:'Save Changes?',
31220    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31221    buttons: Roo.Msg.YESNOCANCEL,
31222    fn: processResult,
31223    animEl: 'elId'
31224 });
31225 </code></pre>
31226  * @singleton
31227  */
31228 Roo.MessageBox = function(){
31229     var dlg, opt, mask, waitTimer;
31230     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31231     var buttons, activeTextEl, bwidth;
31232
31233     // private
31234     var handleButton = function(button){
31235         dlg.hide();
31236         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31237     };
31238
31239     // private
31240     var handleHide = function(){
31241         if(opt && opt.cls){
31242             dlg.el.removeClass(opt.cls);
31243         }
31244         if(waitTimer){
31245             Roo.TaskMgr.stop(waitTimer);
31246             waitTimer = null;
31247         }
31248     };
31249
31250     // private
31251     var updateButtons = function(b){
31252         var width = 0;
31253         if(!b){
31254             buttons["ok"].hide();
31255             buttons["cancel"].hide();
31256             buttons["yes"].hide();
31257             buttons["no"].hide();
31258             dlg.footer.dom.style.display = 'none';
31259             return width;
31260         }
31261         dlg.footer.dom.style.display = '';
31262         for(var k in buttons){
31263             if(typeof buttons[k] != "function"){
31264                 if(b[k]){
31265                     buttons[k].show();
31266                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31267                     width += buttons[k].el.getWidth()+15;
31268                 }else{
31269                     buttons[k].hide();
31270                 }
31271             }
31272         }
31273         return width;
31274     };
31275
31276     // private
31277     var handleEsc = function(d, k, e){
31278         if(opt && opt.closable !== false){
31279             dlg.hide();
31280         }
31281         if(e){
31282             e.stopEvent();
31283         }
31284     };
31285
31286     return {
31287         /**
31288          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31289          * @return {Roo.BasicDialog} The BasicDialog element
31290          */
31291         getDialog : function(){
31292            if(!dlg){
31293                 dlg = new Roo.BasicDialog("x-msg-box", {
31294                     autoCreate : true,
31295                     shadow: true,
31296                     draggable: true,
31297                     resizable:false,
31298                     constraintoviewport:false,
31299                     fixedcenter:true,
31300                     collapsible : false,
31301                     shim:true,
31302                     modal: true,
31303                     width:400, height:100,
31304                     buttonAlign:"center",
31305                     closeClick : function(){
31306                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31307                             handleButton("no");
31308                         }else{
31309                             handleButton("cancel");
31310                         }
31311                     }
31312                 });
31313                 dlg.on("hide", handleHide);
31314                 mask = dlg.mask;
31315                 dlg.addKeyListener(27, handleEsc);
31316                 buttons = {};
31317                 var bt = this.buttonText;
31318                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31319                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31320                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31321                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31322                 bodyEl = dlg.body.createChild({
31323
31324                     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>'
31325                 });
31326                 msgEl = bodyEl.dom.firstChild;
31327                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31328                 textboxEl.enableDisplayMode();
31329                 textboxEl.addKeyListener([10,13], function(){
31330                     if(dlg.isVisible() && opt && opt.buttons){
31331                         if(opt.buttons.ok){
31332                             handleButton("ok");
31333                         }else if(opt.buttons.yes){
31334                             handleButton("yes");
31335                         }
31336                     }
31337                 });
31338                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31339                 textareaEl.enableDisplayMode();
31340                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31341                 progressEl.enableDisplayMode();
31342                 var pf = progressEl.dom.firstChild;
31343                 if (pf) {
31344                     pp = Roo.get(pf.firstChild);
31345                     pp.setHeight(pf.offsetHeight);
31346                 }
31347                 
31348             }
31349             return dlg;
31350         },
31351
31352         /**
31353          * Updates the message box body text
31354          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31355          * the XHTML-compliant non-breaking space character '&amp;#160;')
31356          * @return {Roo.MessageBox} This message box
31357          */
31358         updateText : function(text){
31359             if(!dlg.isVisible() && !opt.width){
31360                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31361             }
31362             msgEl.innerHTML = text || '&#160;';
31363       
31364             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31365             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31366             var w = Math.max(
31367                     Math.min(opt.width || cw , this.maxWidth), 
31368                     Math.max(opt.minWidth || this.minWidth, bwidth)
31369             );
31370             if(opt.prompt){
31371                 activeTextEl.setWidth(w);
31372             }
31373             if(dlg.isVisible()){
31374                 dlg.fixedcenter = false;
31375             }
31376             // to big, make it scroll. = But as usual stupid IE does not support
31377             // !important..
31378             
31379             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31380                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31381                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31382             } else {
31383                 bodyEl.dom.style.height = '';
31384                 bodyEl.dom.style.overflowY = '';
31385             }
31386             if (cw > w) {
31387                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31388             } else {
31389                 bodyEl.dom.style.overflowX = '';
31390             }
31391             
31392             dlg.setContentSize(w, bodyEl.getHeight());
31393             if(dlg.isVisible()){
31394                 dlg.fixedcenter = true;
31395             }
31396             return this;
31397         },
31398
31399         /**
31400          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31401          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31402          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31403          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31404          * @return {Roo.MessageBox} This message box
31405          */
31406         updateProgress : function(value, text){
31407             if(text){
31408                 this.updateText(text);
31409             }
31410             if (pp) { // weird bug on my firefox - for some reason this is not defined
31411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31412             }
31413             return this;
31414         },        
31415
31416         /**
31417          * Returns true if the message box is currently displayed
31418          * @return {Boolean} True if the message box is visible, else false
31419          */
31420         isVisible : function(){
31421             return dlg && dlg.isVisible();  
31422         },
31423
31424         /**
31425          * Hides the message box if it is displayed
31426          */
31427         hide : function(){
31428             if(this.isVisible()){
31429                 dlg.hide();
31430             }  
31431         },
31432
31433         /**
31434          * Displays a new message box, or reinitializes an existing message box, based on the config options
31435          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31436          * The following config object properties are supported:
31437          * <pre>
31438 Property    Type             Description
31439 ----------  ---------------  ------------------------------------------------------------------------------------
31440 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31441                                    closes (defaults to undefined)
31442 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31443                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31444 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31445                                    progress and wait dialogs will ignore this property and always hide the
31446                                    close button as they can only be closed programmatically.
31447 cls               String           A custom CSS class to apply to the message box element
31448 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31449                                    displayed (defaults to 75)
31450 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31451                                    function will be btn (the name of the button that was clicked, if applicable,
31452                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31453                                    Progress and wait dialogs will ignore this option since they do not respond to
31454                                    user actions and can only be closed programmatically, so any required function
31455                                    should be called by the same code after it closes the dialog.
31456 icon              String           A CSS class that provides a background image to be used as an icon for
31457                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31458 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31459 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31460 modal             Boolean          False to allow user interaction with the page while the message box is
31461                                    displayed (defaults to true)
31462 msg               String           A string that will replace the existing message box body text (defaults
31463                                    to the XHTML-compliant non-breaking space character '&#160;')
31464 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31465 progress          Boolean          True to display a progress bar (defaults to false)
31466 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31467 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31468 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31469 title             String           The title text
31470 value             String           The string value to set into the active textbox element if displayed
31471 wait              Boolean          True to display a progress bar (defaults to false)
31472 width             Number           The width of the dialog in pixels
31473 </pre>
31474          *
31475          * Example usage:
31476          * <pre><code>
31477 Roo.Msg.show({
31478    title: 'Address',
31479    msg: 'Please enter your address:',
31480    width: 300,
31481    buttons: Roo.MessageBox.OKCANCEL,
31482    multiline: true,
31483    fn: saveAddress,
31484    animEl: 'addAddressBtn'
31485 });
31486 </code></pre>
31487          * @param {Object} config Configuration options
31488          * @return {Roo.MessageBox} This message box
31489          */
31490         show : function(options)
31491         {
31492             
31493             // this causes nightmares if you show one dialog after another
31494             // especially on callbacks..
31495              
31496             if(this.isVisible()){
31497                 
31498                 this.hide();
31499                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31500                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31501                 Roo.log("New Dialog Message:" +  options.msg )
31502                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31503                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31504                 
31505             }
31506             var d = this.getDialog();
31507             opt = options;
31508             d.setTitle(opt.title || "&#160;");
31509             d.close.setDisplayed(opt.closable !== false);
31510             activeTextEl = textboxEl;
31511             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31512             if(opt.prompt){
31513                 if(opt.multiline){
31514                     textboxEl.hide();
31515                     textareaEl.show();
31516                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31517                         opt.multiline : this.defaultTextHeight);
31518                     activeTextEl = textareaEl;
31519                 }else{
31520                     textboxEl.show();
31521                     textareaEl.hide();
31522                 }
31523             }else{
31524                 textboxEl.hide();
31525                 textareaEl.hide();
31526             }
31527             progressEl.setDisplayed(opt.progress === true);
31528             this.updateProgress(0);
31529             activeTextEl.dom.value = opt.value || "";
31530             if(opt.prompt){
31531                 dlg.setDefaultButton(activeTextEl);
31532             }else{
31533                 var bs = opt.buttons;
31534                 var db = null;
31535                 if(bs && bs.ok){
31536                     db = buttons["ok"];
31537                 }else if(bs && bs.yes){
31538                     db = buttons["yes"];
31539                 }
31540                 dlg.setDefaultButton(db);
31541             }
31542             bwidth = updateButtons(opt.buttons);
31543             this.updateText(opt.msg);
31544             if(opt.cls){
31545                 d.el.addClass(opt.cls);
31546             }
31547             d.proxyDrag = opt.proxyDrag === true;
31548             d.modal = opt.modal !== false;
31549             d.mask = opt.modal !== false ? mask : false;
31550             if(!d.isVisible()){
31551                 // force it to the end of the z-index stack so it gets a cursor in FF
31552                 document.body.appendChild(dlg.el.dom);
31553                 d.animateTarget = null;
31554                 d.show(options.animEl);
31555             }
31556             return this;
31557         },
31558
31559         /**
31560          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31561          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31562          * and closing the message box when the process is complete.
31563          * @param {String} title The title bar text
31564          * @param {String} msg The message box body text
31565          * @return {Roo.MessageBox} This message box
31566          */
31567         progress : function(title, msg){
31568             this.show({
31569                 title : title,
31570                 msg : msg,
31571                 buttons: false,
31572                 progress:true,
31573                 closable:false,
31574                 minWidth: this.minProgressWidth,
31575                 modal : true
31576             });
31577             return this;
31578         },
31579
31580         /**
31581          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31582          * If a callback function is passed it will be called after the user clicks the button, and the
31583          * id of the button that was clicked will be passed as the only parameter to the callback
31584          * (could also be the top-right close button).
31585          * @param {String} title The title bar text
31586          * @param {String} msg The message box body text
31587          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31588          * @param {Object} scope (optional) The scope of the callback function
31589          * @return {Roo.MessageBox} This message box
31590          */
31591         alert : function(title, msg, fn, scope){
31592             this.show({
31593                 title : title,
31594                 msg : msg,
31595                 buttons: this.OK,
31596                 fn: fn,
31597                 scope : scope,
31598                 modal : true
31599             });
31600             return this;
31601         },
31602
31603         /**
31604          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31605          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31606          * You are responsible for closing the message box when the process is complete.
31607          * @param {String} msg The message box body text
31608          * @param {String} title (optional) The title bar text
31609          * @return {Roo.MessageBox} This message box
31610          */
31611         wait : function(msg, title){
31612             this.show({
31613                 title : title,
31614                 msg : msg,
31615                 buttons: false,
31616                 closable:false,
31617                 progress:true,
31618                 modal:true,
31619                 width:300,
31620                 wait:true
31621             });
31622             waitTimer = Roo.TaskMgr.start({
31623                 run: function(i){
31624                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31625                 },
31626                 interval: 1000
31627             });
31628             return this;
31629         },
31630
31631         /**
31632          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31633          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31634          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31635          * @param {String} title The title bar text
31636          * @param {String} msg The message box body text
31637          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31638          * @param {Object} scope (optional) The scope of the callback function
31639          * @return {Roo.MessageBox} This message box
31640          */
31641         confirm : function(title, msg, fn, scope){
31642             this.show({
31643                 title : title,
31644                 msg : msg,
31645                 buttons: this.YESNO,
31646                 fn: fn,
31647                 scope : scope,
31648                 modal : true
31649             });
31650             return this;
31651         },
31652
31653         /**
31654          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31655          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31656          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31657          * (could also be the top-right close button) and the text that was entered will be passed as the two
31658          * parameters to the callback.
31659          * @param {String} title The title bar text
31660          * @param {String} msg The message box body text
31661          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31662          * @param {Object} scope (optional) The scope of the callback function
31663          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31664          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31665          * @return {Roo.MessageBox} This message box
31666          */
31667         prompt : function(title, msg, fn, scope, multiline){
31668             this.show({
31669                 title : title,
31670                 msg : msg,
31671                 buttons: this.OKCANCEL,
31672                 fn: fn,
31673                 minWidth:250,
31674                 scope : scope,
31675                 prompt:true,
31676                 multiline: multiline,
31677                 modal : true
31678             });
31679             return this;
31680         },
31681
31682         /**
31683          * Button config that displays a single OK button
31684          * @type Object
31685          */
31686         OK : {ok:true},
31687         /**
31688          * Button config that displays Yes and No buttons
31689          * @type Object
31690          */
31691         YESNO : {yes:true, no:true},
31692         /**
31693          * Button config that displays OK and Cancel buttons
31694          * @type Object
31695          */
31696         OKCANCEL : {ok:true, cancel:true},
31697         /**
31698          * Button config that displays Yes, No and Cancel buttons
31699          * @type Object
31700          */
31701         YESNOCANCEL : {yes:true, no:true, cancel:true},
31702
31703         /**
31704          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31705          * @type Number
31706          */
31707         defaultTextHeight : 75,
31708         /**
31709          * The maximum width in pixels of the message box (defaults to 600)
31710          * @type Number
31711          */
31712         maxWidth : 600,
31713         /**
31714          * The minimum width in pixels of the message box (defaults to 100)
31715          * @type Number
31716          */
31717         minWidth : 100,
31718         /**
31719          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31720          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31721          * @type Number
31722          */
31723         minProgressWidth : 250,
31724         /**
31725          * An object containing the default button text strings that can be overriden for localized language support.
31726          * Supported properties are: ok, cancel, yes and no.
31727          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31728          * @type Object
31729          */
31730         buttonText : {
31731             ok : "OK",
31732             cancel : "Cancel",
31733             yes : "Yes",
31734             no : "No"
31735         }
31736     };
31737 }();
31738
31739 /**
31740  * Shorthand for {@link Roo.MessageBox}
31741  */
31742 Roo.Msg = Roo.MessageBox;/*
31743  * Based on:
31744  * Ext JS Library 1.1.1
31745  * Copyright(c) 2006-2007, Ext JS, LLC.
31746  *
31747  * Originally Released Under LGPL - original licence link has changed is not relivant.
31748  *
31749  * Fork - LGPL
31750  * <script type="text/javascript">
31751  */
31752 /**
31753  * @class Roo.QuickTips
31754  * Provides attractive and customizable tooltips for any element.
31755  * @singleton
31756  */
31757 Roo.QuickTips = function(){
31758     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31759     var ce, bd, xy, dd;
31760     var visible = false, disabled = true, inited = false;
31761     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31762     
31763     var onOver = function(e){
31764         if(disabled){
31765             return;
31766         }
31767         var t = e.getTarget();
31768         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31769             return;
31770         }
31771         if(ce && t == ce.el){
31772             clearTimeout(hideProc);
31773             return;
31774         }
31775         if(t && tagEls[t.id]){
31776             tagEls[t.id].el = t;
31777             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31778             return;
31779         }
31780         var ttp, et = Roo.fly(t);
31781         var ns = cfg.namespace;
31782         if(tm.interceptTitles && t.title){
31783             ttp = t.title;
31784             t.qtip = ttp;
31785             t.removeAttribute("title");
31786             e.preventDefault();
31787         }else{
31788             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31789         }
31790         if(ttp){
31791             showProc = show.defer(tm.showDelay, tm, [{
31792                 el: t, 
31793                 text: ttp, 
31794                 width: et.getAttributeNS(ns, cfg.width),
31795                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31796                 title: et.getAttributeNS(ns, cfg.title),
31797                     cls: et.getAttributeNS(ns, cfg.cls)
31798             }]);
31799         }
31800     };
31801     
31802     var onOut = function(e){
31803         clearTimeout(showProc);
31804         var t = e.getTarget();
31805         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31806             hideProc = setTimeout(hide, tm.hideDelay);
31807         }
31808     };
31809     
31810     var onMove = function(e){
31811         if(disabled){
31812             return;
31813         }
31814         xy = e.getXY();
31815         xy[1] += 18;
31816         if(tm.trackMouse && ce){
31817             el.setXY(xy);
31818         }
31819     };
31820     
31821     var onDown = function(e){
31822         clearTimeout(showProc);
31823         clearTimeout(hideProc);
31824         if(!e.within(el)){
31825             if(tm.hideOnClick){
31826                 hide();
31827                 tm.disable();
31828                 tm.enable.defer(100, tm);
31829             }
31830         }
31831     };
31832     
31833     var getPad = function(){
31834         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31835     };
31836
31837     var show = function(o){
31838         if(disabled){
31839             return;
31840         }
31841         clearTimeout(dismissProc);
31842         ce = o;
31843         if(removeCls){ // in case manually hidden
31844             el.removeClass(removeCls);
31845             removeCls = null;
31846         }
31847         if(ce.cls){
31848             el.addClass(ce.cls);
31849             removeCls = ce.cls;
31850         }
31851         if(ce.title){
31852             tipTitle.update(ce.title);
31853             tipTitle.show();
31854         }else{
31855             tipTitle.update('');
31856             tipTitle.hide();
31857         }
31858         el.dom.style.width  = tm.maxWidth+'px';
31859         //tipBody.dom.style.width = '';
31860         tipBodyText.update(o.text);
31861         var p = getPad(), w = ce.width;
31862         if(!w){
31863             var td = tipBodyText.dom;
31864             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31865             if(aw > tm.maxWidth){
31866                 w = tm.maxWidth;
31867             }else if(aw < tm.minWidth){
31868                 w = tm.minWidth;
31869             }else{
31870                 w = aw;
31871             }
31872         }
31873         //tipBody.setWidth(w);
31874         el.setWidth(parseInt(w, 10) + p);
31875         if(ce.autoHide === false){
31876             close.setDisplayed(true);
31877             if(dd){
31878                 dd.unlock();
31879             }
31880         }else{
31881             close.setDisplayed(false);
31882             if(dd){
31883                 dd.lock();
31884             }
31885         }
31886         if(xy){
31887             el.avoidY = xy[1]-18;
31888             el.setXY(xy);
31889         }
31890         if(tm.animate){
31891             el.setOpacity(.1);
31892             el.setStyle("visibility", "visible");
31893             el.fadeIn({callback: afterShow});
31894         }else{
31895             afterShow();
31896         }
31897     };
31898     
31899     var afterShow = function(){
31900         if(ce){
31901             el.show();
31902             esc.enable();
31903             if(tm.autoDismiss && ce.autoHide !== false){
31904                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31905             }
31906         }
31907     };
31908     
31909     var hide = function(noanim){
31910         clearTimeout(dismissProc);
31911         clearTimeout(hideProc);
31912         ce = null;
31913         if(el.isVisible()){
31914             esc.disable();
31915             if(noanim !== true && tm.animate){
31916                 el.fadeOut({callback: afterHide});
31917             }else{
31918                 afterHide();
31919             } 
31920         }
31921     };
31922     
31923     var afterHide = function(){
31924         el.hide();
31925         if(removeCls){
31926             el.removeClass(removeCls);
31927             removeCls = null;
31928         }
31929     };
31930     
31931     return {
31932         /**
31933         * @cfg {Number} minWidth
31934         * The minimum width of the quick tip (defaults to 40)
31935         */
31936        minWidth : 40,
31937         /**
31938         * @cfg {Number} maxWidth
31939         * The maximum width of the quick tip (defaults to 300)
31940         */
31941        maxWidth : 300,
31942         /**
31943         * @cfg {Boolean} interceptTitles
31944         * True to automatically use the element's DOM title value if available (defaults to false)
31945         */
31946        interceptTitles : false,
31947         /**
31948         * @cfg {Boolean} trackMouse
31949         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31950         */
31951        trackMouse : false,
31952         /**
31953         * @cfg {Boolean} hideOnClick
31954         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31955         */
31956        hideOnClick : true,
31957         /**
31958         * @cfg {Number} showDelay
31959         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31960         */
31961        showDelay : 500,
31962         /**
31963         * @cfg {Number} hideDelay
31964         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31965         */
31966        hideDelay : 200,
31967         /**
31968         * @cfg {Boolean} autoHide
31969         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31970         * Used in conjunction with hideDelay.
31971         */
31972        autoHide : true,
31973         /**
31974         * @cfg {Boolean}
31975         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31976         * (defaults to true).  Used in conjunction with autoDismissDelay.
31977         */
31978        autoDismiss : true,
31979         /**
31980         * @cfg {Number}
31981         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31982         */
31983        autoDismissDelay : 5000,
31984        /**
31985         * @cfg {Boolean} animate
31986         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31987         */
31988        animate : false,
31989
31990        /**
31991         * @cfg {String} title
31992         * Title text to display (defaults to '').  This can be any valid HTML markup.
31993         */
31994         title: '',
31995        /**
31996         * @cfg {String} text
31997         * Body text to display (defaults to '').  This can be any valid HTML markup.
31998         */
31999         text : '',
32000        /**
32001         * @cfg {String} cls
32002         * A CSS class to apply to the base quick tip element (defaults to '').
32003         */
32004         cls : '',
32005        /**
32006         * @cfg {Number} width
32007         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32008         * minWidth or maxWidth.
32009         */
32010         width : null,
32011
32012     /**
32013      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32014      * or display QuickTips in a page.
32015      */
32016        init : function(){
32017           tm = Roo.QuickTips;
32018           cfg = tm.tagConfig;
32019           if(!inited){
32020               if(!Roo.isReady){ // allow calling of init() before onReady
32021                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32022                   return;
32023               }
32024               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32025               el.fxDefaults = {stopFx: true};
32026               // maximum custom styling
32027               //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>');
32028               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>');              
32029               tipTitle = el.child('h3');
32030               tipTitle.enableDisplayMode("block");
32031               tipBody = el.child('div.x-tip-bd');
32032               tipBodyText = el.child('div.x-tip-bd-inner');
32033               //bdLeft = el.child('div.x-tip-bd-left');
32034               //bdRight = el.child('div.x-tip-bd-right');
32035               close = el.child('div.x-tip-close');
32036               close.enableDisplayMode("block");
32037               close.on("click", hide);
32038               var d = Roo.get(document);
32039               d.on("mousedown", onDown);
32040               d.on("mouseover", onOver);
32041               d.on("mouseout", onOut);
32042               d.on("mousemove", onMove);
32043               esc = d.addKeyListener(27, hide);
32044               esc.disable();
32045               if(Roo.dd.DD){
32046                   dd = el.initDD("default", null, {
32047                       onDrag : function(){
32048                           el.sync();  
32049                       }
32050                   });
32051                   dd.setHandleElId(tipTitle.id);
32052                   dd.lock();
32053               }
32054               inited = true;
32055           }
32056           this.enable(); 
32057        },
32058
32059     /**
32060      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32061      * are supported:
32062      * <pre>
32063 Property    Type                   Description
32064 ----------  ---------------------  ------------------------------------------------------------------------
32065 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32066      * </ul>
32067      * @param {Object} config The config object
32068      */
32069        register : function(config){
32070            var cs = config instanceof Array ? config : arguments;
32071            for(var i = 0, len = cs.length; i < len; i++) {
32072                var c = cs[i];
32073                var target = c.target;
32074                if(target){
32075                    if(target instanceof Array){
32076                        for(var j = 0, jlen = target.length; j < jlen; j++){
32077                            tagEls[target[j]] = c;
32078                        }
32079                    }else{
32080                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32081                    }
32082                }
32083            }
32084        },
32085
32086     /**
32087      * Removes this quick tip from its element and destroys it.
32088      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32089      */
32090        unregister : function(el){
32091            delete tagEls[Roo.id(el)];
32092        },
32093
32094     /**
32095      * Enable this quick tip.
32096      */
32097        enable : function(){
32098            if(inited && disabled){
32099                locks.pop();
32100                if(locks.length < 1){
32101                    disabled = false;
32102                }
32103            }
32104        },
32105
32106     /**
32107      * Disable this quick tip.
32108      */
32109        disable : function(){
32110           disabled = true;
32111           clearTimeout(showProc);
32112           clearTimeout(hideProc);
32113           clearTimeout(dismissProc);
32114           if(ce){
32115               hide(true);
32116           }
32117           locks.push(1);
32118        },
32119
32120     /**
32121      * Returns true if the quick tip is enabled, else false.
32122      */
32123        isEnabled : function(){
32124             return !disabled;
32125        },
32126
32127         // private
32128        tagConfig : {
32129            namespace : "ext",
32130            attribute : "qtip",
32131            width : "width",
32132            target : "target",
32133            title : "qtitle",
32134            hide : "hide",
32135            cls : "qclass"
32136        }
32137    };
32138 }();
32139
32140 // backwards compat
32141 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32142  * Based on:
32143  * Ext JS Library 1.1.1
32144  * Copyright(c) 2006-2007, Ext JS, LLC.
32145  *
32146  * Originally Released Under LGPL - original licence link has changed is not relivant.
32147  *
32148  * Fork - LGPL
32149  * <script type="text/javascript">
32150  */
32151  
32152
32153 /**
32154  * @class Roo.tree.TreePanel
32155  * @extends Roo.data.Tree
32156
32157  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32158  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32159  * @cfg {Boolean} enableDD true to enable drag and drop
32160  * @cfg {Boolean} enableDrag true to enable just drag
32161  * @cfg {Boolean} enableDrop true to enable just drop
32162  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32163  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32164  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32165  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32166  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32167  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32168  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32169  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32170  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32171  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32172  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32173  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32174  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32175  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32176  * @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>
32177  * @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>
32178  * 
32179  * @constructor
32180  * @param {String/HTMLElement/Element} el The container element
32181  * @param {Object} config
32182  */
32183 Roo.tree.TreePanel = function(el, config){
32184     var root = false;
32185     var loader = false;
32186     if (config.root) {
32187         root = config.root;
32188         delete config.root;
32189     }
32190     if (config.loader) {
32191         loader = config.loader;
32192         delete config.loader;
32193     }
32194     
32195     Roo.apply(this, config);
32196     Roo.tree.TreePanel.superclass.constructor.call(this);
32197     this.el = Roo.get(el);
32198     this.el.addClass('x-tree');
32199     //console.log(root);
32200     if (root) {
32201         this.setRootNode( Roo.factory(root, Roo.tree));
32202     }
32203     if (loader) {
32204         this.loader = Roo.factory(loader, Roo.tree);
32205     }
32206    /**
32207     * Read-only. The id of the container element becomes this TreePanel's id.
32208     */
32209     this.id = this.el.id;
32210     this.addEvents({
32211         /**
32212         * @event beforeload
32213         * Fires before a node is loaded, return false to cancel
32214         * @param {Node} node The node being loaded
32215         */
32216         "beforeload" : true,
32217         /**
32218         * @event load
32219         * Fires when a node is loaded
32220         * @param {Node} node The node that was loaded
32221         */
32222         "load" : true,
32223         /**
32224         * @event textchange
32225         * Fires when the text for a node is changed
32226         * @param {Node} node The node
32227         * @param {String} text The new text
32228         * @param {String} oldText The old text
32229         */
32230         "textchange" : true,
32231         /**
32232         * @event beforeexpand
32233         * Fires before a node is expanded, return false to cancel.
32234         * @param {Node} node The node
32235         * @param {Boolean} deep
32236         * @param {Boolean} anim
32237         */
32238         "beforeexpand" : true,
32239         /**
32240         * @event beforecollapse
32241         * Fires before a node is collapsed, return false to cancel.
32242         * @param {Node} node The node
32243         * @param {Boolean} deep
32244         * @param {Boolean} anim
32245         */
32246         "beforecollapse" : true,
32247         /**
32248         * @event expand
32249         * Fires when a node is expanded
32250         * @param {Node} node The node
32251         */
32252         "expand" : true,
32253         /**
32254         * @event disabledchange
32255         * Fires when the disabled status of a node changes
32256         * @param {Node} node The node
32257         * @param {Boolean} disabled
32258         */
32259         "disabledchange" : true,
32260         /**
32261         * @event collapse
32262         * Fires when a node is collapsed
32263         * @param {Node} node The node
32264         */
32265         "collapse" : true,
32266         /**
32267         * @event beforeclick
32268         * Fires before click processing on a node. Return false to cancel the default action.
32269         * @param {Node} node The node
32270         * @param {Roo.EventObject} e The event object
32271         */
32272         "beforeclick":true,
32273         /**
32274         * @event checkchange
32275         * Fires when a node with a checkbox's checked property changes
32276         * @param {Node} this This node
32277         * @param {Boolean} checked
32278         */
32279         "checkchange":true,
32280         /**
32281         * @event click
32282         * Fires when a node is clicked
32283         * @param {Node} node The node
32284         * @param {Roo.EventObject} e The event object
32285         */
32286         "click":true,
32287         /**
32288         * @event dblclick
32289         * Fires when a node is double clicked
32290         * @param {Node} node The node
32291         * @param {Roo.EventObject} e The event object
32292         */
32293         "dblclick":true,
32294         /**
32295         * @event contextmenu
32296         * Fires when a node is right clicked
32297         * @param {Node} node The node
32298         * @param {Roo.EventObject} e The event object
32299         */
32300         "contextmenu":true,
32301         /**
32302         * @event beforechildrenrendered
32303         * Fires right before the child nodes for a node are rendered
32304         * @param {Node} node The node
32305         */
32306         "beforechildrenrendered":true,
32307         /**
32308         * @event startdrag
32309         * Fires when a node starts being dragged
32310         * @param {Roo.tree.TreePanel} this
32311         * @param {Roo.tree.TreeNode} node
32312         * @param {event} e The raw browser event
32313         */ 
32314        "startdrag" : true,
32315        /**
32316         * @event enddrag
32317         * Fires when a drag operation is complete
32318         * @param {Roo.tree.TreePanel} this
32319         * @param {Roo.tree.TreeNode} node
32320         * @param {event} e The raw browser event
32321         */
32322        "enddrag" : true,
32323        /**
32324         * @event dragdrop
32325         * Fires when a dragged node is dropped on a valid DD target
32326         * @param {Roo.tree.TreePanel} this
32327         * @param {Roo.tree.TreeNode} node
32328         * @param {DD} dd The dd it was dropped on
32329         * @param {event} e The raw browser event
32330         */
32331        "dragdrop" : true,
32332        /**
32333         * @event beforenodedrop
32334         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32335         * passed to handlers has the following properties:<br />
32336         * <ul style="padding:5px;padding-left:16px;">
32337         * <li>tree - The TreePanel</li>
32338         * <li>target - The node being targeted for the drop</li>
32339         * <li>data - The drag data from the drag source</li>
32340         * <li>point - The point of the drop - append, above or below</li>
32341         * <li>source - The drag source</li>
32342         * <li>rawEvent - Raw mouse event</li>
32343         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32344         * to be inserted by setting them on this object.</li>
32345         * <li>cancel - Set this to true to cancel the drop.</li>
32346         * </ul>
32347         * @param {Object} dropEvent
32348         */
32349        "beforenodedrop" : true,
32350        /**
32351         * @event nodedrop
32352         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32353         * passed to handlers has the following properties:<br />
32354         * <ul style="padding:5px;padding-left:16px;">
32355         * <li>tree - The TreePanel</li>
32356         * <li>target - The node being targeted for the drop</li>
32357         * <li>data - The drag data from the drag source</li>
32358         * <li>point - The point of the drop - append, above or below</li>
32359         * <li>source - The drag source</li>
32360         * <li>rawEvent - Raw mouse event</li>
32361         * <li>dropNode - Dropped node(s).</li>
32362         * </ul>
32363         * @param {Object} dropEvent
32364         */
32365        "nodedrop" : true,
32366         /**
32367         * @event nodedragover
32368         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32369         * passed to handlers has the following properties:<br />
32370         * <ul style="padding:5px;padding-left:16px;">
32371         * <li>tree - The TreePanel</li>
32372         * <li>target - The node being targeted for the drop</li>
32373         * <li>data - The drag data from the drag source</li>
32374         * <li>point - The point of the drop - append, above or below</li>
32375         * <li>source - The drag source</li>
32376         * <li>rawEvent - Raw mouse event</li>
32377         * <li>dropNode - Drop node(s) provided by the source.</li>
32378         * <li>cancel - Set this to true to signal drop not allowed.</li>
32379         * </ul>
32380         * @param {Object} dragOverEvent
32381         */
32382        "nodedragover" : true
32383         
32384     });
32385     if(this.singleExpand){
32386        this.on("beforeexpand", this.restrictExpand, this);
32387     }
32388     if (this.editor) {
32389         this.editor.tree = this;
32390         this.editor = Roo.factory(this.editor, Roo.tree);
32391     }
32392     
32393     if (this.selModel) {
32394         this.selModel = Roo.factory(this.selModel, Roo.tree);
32395     }
32396    
32397 };
32398 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32399     rootVisible : true,
32400     animate: Roo.enableFx,
32401     lines : true,
32402     enableDD : false,
32403     hlDrop : Roo.enableFx,
32404   
32405     renderer: false,
32406     
32407     rendererTip: false,
32408     // private
32409     restrictExpand : function(node){
32410         var p = node.parentNode;
32411         if(p){
32412             if(p.expandedChild && p.expandedChild.parentNode == p){
32413                 p.expandedChild.collapse();
32414             }
32415             p.expandedChild = node;
32416         }
32417     },
32418
32419     // private override
32420     setRootNode : function(node){
32421         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32422         if(!this.rootVisible){
32423             node.ui = new Roo.tree.RootTreeNodeUI(node);
32424         }
32425         return node;
32426     },
32427
32428     /**
32429      * Returns the container element for this TreePanel
32430      */
32431     getEl : function(){
32432         return this.el;
32433     },
32434
32435     /**
32436      * Returns the default TreeLoader for this TreePanel
32437      */
32438     getLoader : function(){
32439         return this.loader;
32440     },
32441
32442     /**
32443      * Expand all nodes
32444      */
32445     expandAll : function(){
32446         this.root.expand(true);
32447     },
32448
32449     /**
32450      * Collapse all nodes
32451      */
32452     collapseAll : function(){
32453         this.root.collapse(true);
32454     },
32455
32456     /**
32457      * Returns the selection model used by this TreePanel
32458      */
32459     getSelectionModel : function(){
32460         if(!this.selModel){
32461             this.selModel = new Roo.tree.DefaultSelectionModel();
32462         }
32463         return this.selModel;
32464     },
32465
32466     /**
32467      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32468      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32469      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32470      * @return {Array}
32471      */
32472     getChecked : function(a, startNode){
32473         startNode = startNode || this.root;
32474         var r = [];
32475         var f = function(){
32476             if(this.attributes.checked){
32477                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32478             }
32479         }
32480         startNode.cascade(f);
32481         return r;
32482     },
32483
32484     /**
32485      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32486      * @param {String} path
32487      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32488      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32489      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32490      */
32491     expandPath : function(path, attr, callback){
32492         attr = attr || "id";
32493         var keys = path.split(this.pathSeparator);
32494         var curNode = this.root;
32495         if(curNode.attributes[attr] != keys[1]){ // invalid root
32496             if(callback){
32497                 callback(false, null);
32498             }
32499             return;
32500         }
32501         var index = 1;
32502         var f = function(){
32503             if(++index == keys.length){
32504                 if(callback){
32505                     callback(true, curNode);
32506                 }
32507                 return;
32508             }
32509             var c = curNode.findChild(attr, keys[index]);
32510             if(!c){
32511                 if(callback){
32512                     callback(false, curNode);
32513                 }
32514                 return;
32515             }
32516             curNode = c;
32517             c.expand(false, false, f);
32518         };
32519         curNode.expand(false, false, f);
32520     },
32521
32522     /**
32523      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32524      * @param {String} path
32525      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32526      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32527      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32528      */
32529     selectPath : function(path, attr, callback){
32530         attr = attr || "id";
32531         var keys = path.split(this.pathSeparator);
32532         var v = keys.pop();
32533         if(keys.length > 0){
32534             var f = function(success, node){
32535                 if(success && node){
32536                     var n = node.findChild(attr, v);
32537                     if(n){
32538                         n.select();
32539                         if(callback){
32540                             callback(true, n);
32541                         }
32542                     }else if(callback){
32543                         callback(false, n);
32544                     }
32545                 }else{
32546                     if(callback){
32547                         callback(false, n);
32548                     }
32549                 }
32550             };
32551             this.expandPath(keys.join(this.pathSeparator), attr, f);
32552         }else{
32553             this.root.select();
32554             if(callback){
32555                 callback(true, this.root);
32556             }
32557         }
32558     },
32559
32560     getTreeEl : function(){
32561         return this.el;
32562     },
32563
32564     /**
32565      * Trigger rendering of this TreePanel
32566      */
32567     render : function(){
32568         if (this.innerCt) {
32569             return this; // stop it rendering more than once!!
32570         }
32571         
32572         this.innerCt = this.el.createChild({tag:"ul",
32573                cls:"x-tree-root-ct " +
32574                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32575
32576         if(this.containerScroll){
32577             Roo.dd.ScrollManager.register(this.el);
32578         }
32579         if((this.enableDD || this.enableDrop) && !this.dropZone){
32580            /**
32581             * The dropZone used by this tree if drop is enabled
32582             * @type Roo.tree.TreeDropZone
32583             */
32584              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32585                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32586            });
32587         }
32588         if((this.enableDD || this.enableDrag) && !this.dragZone){
32589            /**
32590             * The dragZone used by this tree if drag is enabled
32591             * @type Roo.tree.TreeDragZone
32592             */
32593             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32594                ddGroup: this.ddGroup || "TreeDD",
32595                scroll: this.ddScroll
32596            });
32597         }
32598         this.getSelectionModel().init(this);
32599         if (!this.root) {
32600             Roo.log("ROOT not set in tree");
32601             return this;
32602         }
32603         this.root.render();
32604         if(!this.rootVisible){
32605             this.root.renderChildren();
32606         }
32607         return this;
32608     }
32609 });/*
32610  * Based on:
32611  * Ext JS Library 1.1.1
32612  * Copyright(c) 2006-2007, Ext JS, LLC.
32613  *
32614  * Originally Released Under LGPL - original licence link has changed is not relivant.
32615  *
32616  * Fork - LGPL
32617  * <script type="text/javascript">
32618  */
32619  
32620
32621 /**
32622  * @class Roo.tree.DefaultSelectionModel
32623  * @extends Roo.util.Observable
32624  * The default single selection for a TreePanel.
32625  * @param {Object} cfg Configuration
32626  */
32627 Roo.tree.DefaultSelectionModel = function(cfg){
32628    this.selNode = null;
32629    
32630    
32631    
32632    this.addEvents({
32633        /**
32634         * @event selectionchange
32635         * Fires when the selected node changes
32636         * @param {DefaultSelectionModel} this
32637         * @param {TreeNode} node the new selection
32638         */
32639        "selectionchange" : true,
32640
32641        /**
32642         * @event beforeselect
32643         * Fires before the selected node changes, return false to cancel the change
32644         * @param {DefaultSelectionModel} this
32645         * @param {TreeNode} node the new selection
32646         * @param {TreeNode} node the old selection
32647         */
32648        "beforeselect" : true
32649    });
32650    
32651     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32652 };
32653
32654 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32655     init : function(tree){
32656         this.tree = tree;
32657         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32658         tree.on("click", this.onNodeClick, this);
32659     },
32660     
32661     onNodeClick : function(node, e){
32662         if (e.ctrlKey && this.selNode == node)  {
32663             this.unselect(node);
32664             return;
32665         }
32666         this.select(node);
32667     },
32668     
32669     /**
32670      * Select a node.
32671      * @param {TreeNode} node The node to select
32672      * @return {TreeNode} The selected node
32673      */
32674     select : function(node){
32675         var last = this.selNode;
32676         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32677             if(last){
32678                 last.ui.onSelectedChange(false);
32679             }
32680             this.selNode = node;
32681             node.ui.onSelectedChange(true);
32682             this.fireEvent("selectionchange", this, node, last);
32683         }
32684         return node;
32685     },
32686     
32687     /**
32688      * Deselect a node.
32689      * @param {TreeNode} node The node to unselect
32690      */
32691     unselect : function(node){
32692         if(this.selNode == node){
32693             this.clearSelections();
32694         }    
32695     },
32696     
32697     /**
32698      * Clear all selections
32699      */
32700     clearSelections : function(){
32701         var n = this.selNode;
32702         if(n){
32703             n.ui.onSelectedChange(false);
32704             this.selNode = null;
32705             this.fireEvent("selectionchange", this, null);
32706         }
32707         return n;
32708     },
32709     
32710     /**
32711      * Get the selected node
32712      * @return {TreeNode} The selected node
32713      */
32714     getSelectedNode : function(){
32715         return this.selNode;    
32716     },
32717     
32718     /**
32719      * Returns true if the node is selected
32720      * @param {TreeNode} node The node to check
32721      * @return {Boolean}
32722      */
32723     isSelected : function(node){
32724         return this.selNode == node;  
32725     },
32726
32727     /**
32728      * Selects the node above the selected node in the tree, intelligently walking the nodes
32729      * @return TreeNode The new selection
32730      */
32731     selectPrevious : function(){
32732         var s = this.selNode || this.lastSelNode;
32733         if(!s){
32734             return null;
32735         }
32736         var ps = s.previousSibling;
32737         if(ps){
32738             if(!ps.isExpanded() || ps.childNodes.length < 1){
32739                 return this.select(ps);
32740             } else{
32741                 var lc = ps.lastChild;
32742                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32743                     lc = lc.lastChild;
32744                 }
32745                 return this.select(lc);
32746             }
32747         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32748             return this.select(s.parentNode);
32749         }
32750         return null;
32751     },
32752
32753     /**
32754      * Selects the node above the selected node in the tree, intelligently walking the nodes
32755      * @return TreeNode The new selection
32756      */
32757     selectNext : function(){
32758         var s = this.selNode || this.lastSelNode;
32759         if(!s){
32760             return null;
32761         }
32762         if(s.firstChild && s.isExpanded()){
32763              return this.select(s.firstChild);
32764          }else if(s.nextSibling){
32765              return this.select(s.nextSibling);
32766          }else if(s.parentNode){
32767             var newS = null;
32768             s.parentNode.bubble(function(){
32769                 if(this.nextSibling){
32770                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32771                     return false;
32772                 }
32773             });
32774             return newS;
32775          }
32776         return null;
32777     },
32778
32779     onKeyDown : function(e){
32780         var s = this.selNode || this.lastSelNode;
32781         // undesirable, but required
32782         var sm = this;
32783         if(!s){
32784             return;
32785         }
32786         var k = e.getKey();
32787         switch(k){
32788              case e.DOWN:
32789                  e.stopEvent();
32790                  this.selectNext();
32791              break;
32792              case e.UP:
32793                  e.stopEvent();
32794                  this.selectPrevious();
32795              break;
32796              case e.RIGHT:
32797                  e.preventDefault();
32798                  if(s.hasChildNodes()){
32799                      if(!s.isExpanded()){
32800                          s.expand();
32801                      }else if(s.firstChild){
32802                          this.select(s.firstChild, e);
32803                      }
32804                  }
32805              break;
32806              case e.LEFT:
32807                  e.preventDefault();
32808                  if(s.hasChildNodes() && s.isExpanded()){
32809                      s.collapse();
32810                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32811                      this.select(s.parentNode, e);
32812                  }
32813              break;
32814         };
32815     }
32816 });
32817
32818 /**
32819  * @class Roo.tree.MultiSelectionModel
32820  * @extends Roo.util.Observable
32821  * Multi selection for a TreePanel.
32822  * @param {Object} cfg Configuration
32823  */
32824 Roo.tree.MultiSelectionModel = function(){
32825    this.selNodes = [];
32826    this.selMap = {};
32827    this.addEvents({
32828        /**
32829         * @event selectionchange
32830         * Fires when the selected nodes change
32831         * @param {MultiSelectionModel} this
32832         * @param {Array} nodes Array of the selected nodes
32833         */
32834        "selectionchange" : true
32835    });
32836    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32837    
32838 };
32839
32840 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32841     init : function(tree){
32842         this.tree = tree;
32843         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32844         tree.on("click", this.onNodeClick, this);
32845     },
32846     
32847     onNodeClick : function(node, e){
32848         this.select(node, e, e.ctrlKey);
32849     },
32850     
32851     /**
32852      * Select a node.
32853      * @param {TreeNode} node The node to select
32854      * @param {EventObject} e (optional) An event associated with the selection
32855      * @param {Boolean} keepExisting True to retain existing selections
32856      * @return {TreeNode} The selected node
32857      */
32858     select : function(node, e, keepExisting){
32859         if(keepExisting !== true){
32860             this.clearSelections(true);
32861         }
32862         if(this.isSelected(node)){
32863             this.lastSelNode = node;
32864             return node;
32865         }
32866         this.selNodes.push(node);
32867         this.selMap[node.id] = node;
32868         this.lastSelNode = node;
32869         node.ui.onSelectedChange(true);
32870         this.fireEvent("selectionchange", this, this.selNodes);
32871         return node;
32872     },
32873     
32874     /**
32875      * Deselect a node.
32876      * @param {TreeNode} node The node to unselect
32877      */
32878     unselect : function(node){
32879         if(this.selMap[node.id]){
32880             node.ui.onSelectedChange(false);
32881             var sn = this.selNodes;
32882             var index = -1;
32883             if(sn.indexOf){
32884                 index = sn.indexOf(node);
32885             }else{
32886                 for(var i = 0, len = sn.length; i < len; i++){
32887                     if(sn[i] == node){
32888                         index = i;
32889                         break;
32890                     }
32891                 }
32892             }
32893             if(index != -1){
32894                 this.selNodes.splice(index, 1);
32895             }
32896             delete this.selMap[node.id];
32897             this.fireEvent("selectionchange", this, this.selNodes);
32898         }
32899     },
32900     
32901     /**
32902      * Clear all selections
32903      */
32904     clearSelections : function(suppressEvent){
32905         var sn = this.selNodes;
32906         if(sn.length > 0){
32907             for(var i = 0, len = sn.length; i < len; i++){
32908                 sn[i].ui.onSelectedChange(false);
32909             }
32910             this.selNodes = [];
32911             this.selMap = {};
32912             if(suppressEvent !== true){
32913                 this.fireEvent("selectionchange", this, this.selNodes);
32914             }
32915         }
32916     },
32917     
32918     /**
32919      * Returns true if the node is selected
32920      * @param {TreeNode} node The node to check
32921      * @return {Boolean}
32922      */
32923     isSelected : function(node){
32924         return this.selMap[node.id] ? true : false;  
32925     },
32926     
32927     /**
32928      * Returns an array of the selected nodes
32929      * @return {Array}
32930      */
32931     getSelectedNodes : function(){
32932         return this.selNodes;    
32933     },
32934
32935     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32936
32937     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32938
32939     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32940 });/*
32941  * Based on:
32942  * Ext JS Library 1.1.1
32943  * Copyright(c) 2006-2007, Ext JS, LLC.
32944  *
32945  * Originally Released Under LGPL - original licence link has changed is not relivant.
32946  *
32947  * Fork - LGPL
32948  * <script type="text/javascript">
32949  */
32950  
32951 /**
32952  * @class Roo.tree.TreeNode
32953  * @extends Roo.data.Node
32954  * @cfg {String} text The text for this node
32955  * @cfg {Boolean} expanded true to start the node expanded
32956  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32957  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32958  * @cfg {Boolean} disabled true to start the node disabled
32959  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32960  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32961  * @cfg {String} cls A css class to be added to the node
32962  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32963  * @cfg {String} href URL of the link used for the node (defaults to #)
32964  * @cfg {String} hrefTarget target frame for the link
32965  * @cfg {String} qtip An Ext QuickTip for the node
32966  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32967  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32968  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32969  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32970  * (defaults to undefined with no checkbox rendered)
32971  * @constructor
32972  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32973  */
32974 Roo.tree.TreeNode = function(attributes){
32975     attributes = attributes || {};
32976     if(typeof attributes == "string"){
32977         attributes = {text: attributes};
32978     }
32979     this.childrenRendered = false;
32980     this.rendered = false;
32981     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32982     this.expanded = attributes.expanded === true;
32983     this.isTarget = attributes.isTarget !== false;
32984     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32985     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32986
32987     /**
32988      * Read-only. The text for this node. To change it use setText().
32989      * @type String
32990      */
32991     this.text = attributes.text;
32992     /**
32993      * True if this node is disabled.
32994      * @type Boolean
32995      */
32996     this.disabled = attributes.disabled === true;
32997
32998     this.addEvents({
32999         /**
33000         * @event textchange
33001         * Fires when the text for this node is changed
33002         * @param {Node} this This node
33003         * @param {String} text The new text
33004         * @param {String} oldText The old text
33005         */
33006         "textchange" : true,
33007         /**
33008         * @event beforeexpand
33009         * Fires before this node is expanded, return false to cancel.
33010         * @param {Node} this This node
33011         * @param {Boolean} deep
33012         * @param {Boolean} anim
33013         */
33014         "beforeexpand" : true,
33015         /**
33016         * @event beforecollapse
33017         * Fires before this node is collapsed, return false to cancel.
33018         * @param {Node} this This node
33019         * @param {Boolean} deep
33020         * @param {Boolean} anim
33021         */
33022         "beforecollapse" : true,
33023         /**
33024         * @event expand
33025         * Fires when this node is expanded
33026         * @param {Node} this This node
33027         */
33028         "expand" : true,
33029         /**
33030         * @event disabledchange
33031         * Fires when the disabled status of this node changes
33032         * @param {Node} this This node
33033         * @param {Boolean} disabled
33034         */
33035         "disabledchange" : true,
33036         /**
33037         * @event collapse
33038         * Fires when this node is collapsed
33039         * @param {Node} this This node
33040         */
33041         "collapse" : true,
33042         /**
33043         * @event beforeclick
33044         * Fires before click processing. Return false to cancel the default action.
33045         * @param {Node} this This node
33046         * @param {Roo.EventObject} e The event object
33047         */
33048         "beforeclick":true,
33049         /**
33050         * @event checkchange
33051         * Fires when a node with a checkbox's checked property changes
33052         * @param {Node} this This node
33053         * @param {Boolean} checked
33054         */
33055         "checkchange":true,
33056         /**
33057         * @event click
33058         * Fires when this node is clicked
33059         * @param {Node} this This node
33060         * @param {Roo.EventObject} e The event object
33061         */
33062         "click":true,
33063         /**
33064         * @event dblclick
33065         * Fires when this node is double clicked
33066         * @param {Node} this This node
33067         * @param {Roo.EventObject} e The event object
33068         */
33069         "dblclick":true,
33070         /**
33071         * @event contextmenu
33072         * Fires when this node is right clicked
33073         * @param {Node} this This node
33074         * @param {Roo.EventObject} e The event object
33075         */
33076         "contextmenu":true,
33077         /**
33078         * @event beforechildrenrendered
33079         * Fires right before the child nodes for this node are rendered
33080         * @param {Node} this This node
33081         */
33082         "beforechildrenrendered":true
33083     });
33084
33085     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33086
33087     /**
33088      * Read-only. The UI for this node
33089      * @type TreeNodeUI
33090      */
33091     this.ui = new uiClass(this);
33092     
33093     // finally support items[]
33094     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33095         return;
33096     }
33097     
33098     
33099     Roo.each(this.attributes.items, function(c) {
33100         this.appendChild(Roo.factory(c,Roo.Tree));
33101     }, this);
33102     delete this.attributes.items;
33103     
33104     
33105     
33106 };
33107 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33108     preventHScroll: true,
33109     /**
33110      * Returns true if this node is expanded
33111      * @return {Boolean}
33112      */
33113     isExpanded : function(){
33114         return this.expanded;
33115     },
33116
33117     /**
33118      * Returns the UI object for this node
33119      * @return {TreeNodeUI}
33120      */
33121     getUI : function(){
33122         return this.ui;
33123     },
33124
33125     // private override
33126     setFirstChild : function(node){
33127         var of = this.firstChild;
33128         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33129         if(this.childrenRendered && of && node != of){
33130             of.renderIndent(true, true);
33131         }
33132         if(this.rendered){
33133             this.renderIndent(true, true);
33134         }
33135     },
33136
33137     // private override
33138     setLastChild : function(node){
33139         var ol = this.lastChild;
33140         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33141         if(this.childrenRendered && ol && node != ol){
33142             ol.renderIndent(true, true);
33143         }
33144         if(this.rendered){
33145             this.renderIndent(true, true);
33146         }
33147     },
33148
33149     // these methods are overridden to provide lazy rendering support
33150     // private override
33151     appendChild : function()
33152     {
33153         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33154         if(node && this.childrenRendered){
33155             node.render();
33156         }
33157         this.ui.updateExpandIcon();
33158         return node;
33159     },
33160
33161     // private override
33162     removeChild : function(node){
33163         this.ownerTree.getSelectionModel().unselect(node);
33164         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33165         // if it's been rendered remove dom node
33166         if(this.childrenRendered){
33167             node.ui.remove();
33168         }
33169         if(this.childNodes.length < 1){
33170             this.collapse(false, false);
33171         }else{
33172             this.ui.updateExpandIcon();
33173         }
33174         if(!this.firstChild) {
33175             this.childrenRendered = false;
33176         }
33177         return node;
33178     },
33179
33180     // private override
33181     insertBefore : function(node, refNode){
33182         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33183         if(newNode && refNode && this.childrenRendered){
33184             node.render();
33185         }
33186         this.ui.updateExpandIcon();
33187         return newNode;
33188     },
33189
33190     /**
33191      * Sets the text for this node
33192      * @param {String} text
33193      */
33194     setText : function(text){
33195         var oldText = this.text;
33196         this.text = text;
33197         this.attributes.text = text;
33198         if(this.rendered){ // event without subscribing
33199             this.ui.onTextChange(this, text, oldText);
33200         }
33201         this.fireEvent("textchange", this, text, oldText);
33202     },
33203
33204     /**
33205      * Triggers selection of this node
33206      */
33207     select : function(){
33208         this.getOwnerTree().getSelectionModel().select(this);
33209     },
33210
33211     /**
33212      * Triggers deselection of this node
33213      */
33214     unselect : function(){
33215         this.getOwnerTree().getSelectionModel().unselect(this);
33216     },
33217
33218     /**
33219      * Returns true if this node is selected
33220      * @return {Boolean}
33221      */
33222     isSelected : function(){
33223         return this.getOwnerTree().getSelectionModel().isSelected(this);
33224     },
33225
33226     /**
33227      * Expand this node.
33228      * @param {Boolean} deep (optional) True to expand all children as well
33229      * @param {Boolean} anim (optional) false to cancel the default animation
33230      * @param {Function} callback (optional) A callback to be called when
33231      * expanding this node completes (does not wait for deep expand to complete).
33232      * Called with 1 parameter, this node.
33233      */
33234     expand : function(deep, anim, callback){
33235         if(!this.expanded){
33236             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33237                 return;
33238             }
33239             if(!this.childrenRendered){
33240                 this.renderChildren();
33241             }
33242             this.expanded = true;
33243             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33244                 this.ui.animExpand(function(){
33245                     this.fireEvent("expand", this);
33246                     if(typeof callback == "function"){
33247                         callback(this);
33248                     }
33249                     if(deep === true){
33250                         this.expandChildNodes(true);
33251                     }
33252                 }.createDelegate(this));
33253                 return;
33254             }else{
33255                 this.ui.expand();
33256                 this.fireEvent("expand", this);
33257                 if(typeof callback == "function"){
33258                     callback(this);
33259                 }
33260             }
33261         }else{
33262            if(typeof callback == "function"){
33263                callback(this);
33264            }
33265         }
33266         if(deep === true){
33267             this.expandChildNodes(true);
33268         }
33269     },
33270
33271     isHiddenRoot : function(){
33272         return this.isRoot && !this.getOwnerTree().rootVisible;
33273     },
33274
33275     /**
33276      * Collapse this node.
33277      * @param {Boolean} deep (optional) True to collapse all children as well
33278      * @param {Boolean} anim (optional) false to cancel the default animation
33279      */
33280     collapse : function(deep, anim){
33281         if(this.expanded && !this.isHiddenRoot()){
33282             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33283                 return;
33284             }
33285             this.expanded = false;
33286             if((this.getOwnerTree().animate && anim !== false) || anim){
33287                 this.ui.animCollapse(function(){
33288                     this.fireEvent("collapse", this);
33289                     if(deep === true){
33290                         this.collapseChildNodes(true);
33291                     }
33292                 }.createDelegate(this));
33293                 return;
33294             }else{
33295                 this.ui.collapse();
33296                 this.fireEvent("collapse", this);
33297             }
33298         }
33299         if(deep === true){
33300             var cs = this.childNodes;
33301             for(var i = 0, len = cs.length; i < len; i++) {
33302                 cs[i].collapse(true, false);
33303             }
33304         }
33305     },
33306
33307     // private
33308     delayedExpand : function(delay){
33309         if(!this.expandProcId){
33310             this.expandProcId = this.expand.defer(delay, this);
33311         }
33312     },
33313
33314     // private
33315     cancelExpand : function(){
33316         if(this.expandProcId){
33317             clearTimeout(this.expandProcId);
33318         }
33319         this.expandProcId = false;
33320     },
33321
33322     /**
33323      * Toggles expanded/collapsed state of the node
33324      */
33325     toggle : function(){
33326         if(this.expanded){
33327             this.collapse();
33328         }else{
33329             this.expand();
33330         }
33331     },
33332
33333     /**
33334      * Ensures all parent nodes are expanded
33335      */
33336     ensureVisible : function(callback){
33337         var tree = this.getOwnerTree();
33338         tree.expandPath(this.parentNode.getPath(), false, function(){
33339             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33340             Roo.callback(callback);
33341         }.createDelegate(this));
33342     },
33343
33344     /**
33345      * Expand all child nodes
33346      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33347      */
33348     expandChildNodes : function(deep){
33349         var cs = this.childNodes;
33350         for(var i = 0, len = cs.length; i < len; i++) {
33351                 cs[i].expand(deep);
33352         }
33353     },
33354
33355     /**
33356      * Collapse all child nodes
33357      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33358      */
33359     collapseChildNodes : function(deep){
33360         var cs = this.childNodes;
33361         for(var i = 0, len = cs.length; i < len; i++) {
33362                 cs[i].collapse(deep);
33363         }
33364     },
33365
33366     /**
33367      * Disables this node
33368      */
33369     disable : function(){
33370         this.disabled = true;
33371         this.unselect();
33372         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33373             this.ui.onDisableChange(this, true);
33374         }
33375         this.fireEvent("disabledchange", this, true);
33376     },
33377
33378     /**
33379      * Enables this node
33380      */
33381     enable : function(){
33382         this.disabled = false;
33383         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33384             this.ui.onDisableChange(this, false);
33385         }
33386         this.fireEvent("disabledchange", this, false);
33387     },
33388
33389     // private
33390     renderChildren : function(suppressEvent){
33391         if(suppressEvent !== false){
33392             this.fireEvent("beforechildrenrendered", this);
33393         }
33394         var cs = this.childNodes;
33395         for(var i = 0, len = cs.length; i < len; i++){
33396             cs[i].render(true);
33397         }
33398         this.childrenRendered = true;
33399     },
33400
33401     // private
33402     sort : function(fn, scope){
33403         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33404         if(this.childrenRendered){
33405             var cs = this.childNodes;
33406             for(var i = 0, len = cs.length; i < len; i++){
33407                 cs[i].render(true);
33408             }
33409         }
33410     },
33411
33412     // private
33413     render : function(bulkRender){
33414         this.ui.render(bulkRender);
33415         if(!this.rendered){
33416             this.rendered = true;
33417             if(this.expanded){
33418                 this.expanded = false;
33419                 this.expand(false, false);
33420             }
33421         }
33422     },
33423
33424     // private
33425     renderIndent : function(deep, refresh){
33426         if(refresh){
33427             this.ui.childIndent = null;
33428         }
33429         this.ui.renderIndent();
33430         if(deep === true && this.childrenRendered){
33431             var cs = this.childNodes;
33432             for(var i = 0, len = cs.length; i < len; i++){
33433                 cs[i].renderIndent(true, refresh);
33434             }
33435         }
33436     }
33437 });/*
33438  * Based on:
33439  * Ext JS Library 1.1.1
33440  * Copyright(c) 2006-2007, Ext JS, LLC.
33441  *
33442  * Originally Released Under LGPL - original licence link has changed is not relivant.
33443  *
33444  * Fork - LGPL
33445  * <script type="text/javascript">
33446  */
33447  
33448 /**
33449  * @class Roo.tree.AsyncTreeNode
33450  * @extends Roo.tree.TreeNode
33451  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33452  * @constructor
33453  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33454  */
33455  Roo.tree.AsyncTreeNode = function(config){
33456     this.loaded = false;
33457     this.loading = false;
33458     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33459     /**
33460     * @event beforeload
33461     * Fires before this node is loaded, return false to cancel
33462     * @param {Node} this This node
33463     */
33464     this.addEvents({'beforeload':true, 'load': true});
33465     /**
33466     * @event load
33467     * Fires when this node is loaded
33468     * @param {Node} this This node
33469     */
33470     /**
33471      * The loader used by this node (defaults to using the tree's defined loader)
33472      * @type TreeLoader
33473      * @property loader
33474      */
33475 };
33476 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33477     expand : function(deep, anim, callback){
33478         if(this.loading){ // if an async load is already running, waiting til it's done
33479             var timer;
33480             var f = function(){
33481                 if(!this.loading){ // done loading
33482                     clearInterval(timer);
33483                     this.expand(deep, anim, callback);
33484                 }
33485             }.createDelegate(this);
33486             timer = setInterval(f, 200);
33487             return;
33488         }
33489         if(!this.loaded){
33490             if(this.fireEvent("beforeload", this) === false){
33491                 return;
33492             }
33493             this.loading = true;
33494             this.ui.beforeLoad(this);
33495             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33496             if(loader){
33497                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33498                 return;
33499             }
33500         }
33501         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33502     },
33503     
33504     /**
33505      * Returns true if this node is currently loading
33506      * @return {Boolean}
33507      */
33508     isLoading : function(){
33509         return this.loading;  
33510     },
33511     
33512     loadComplete : function(deep, anim, callback){
33513         this.loading = false;
33514         this.loaded = true;
33515         this.ui.afterLoad(this);
33516         this.fireEvent("load", this);
33517         this.expand(deep, anim, callback);
33518     },
33519     
33520     /**
33521      * Returns true if this node has been loaded
33522      * @return {Boolean}
33523      */
33524     isLoaded : function(){
33525         return this.loaded;
33526     },
33527     
33528     hasChildNodes : function(){
33529         if(!this.isLeaf() && !this.loaded){
33530             return true;
33531         }else{
33532             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33533         }
33534     },
33535
33536     /**
33537      * Trigger a reload for this node
33538      * @param {Function} callback
33539      */
33540     reload : function(callback){
33541         this.collapse(false, false);
33542         while(this.firstChild){
33543             this.removeChild(this.firstChild);
33544         }
33545         this.childrenRendered = false;
33546         this.loaded = false;
33547         if(this.isHiddenRoot()){
33548             this.expanded = false;
33549         }
33550         this.expand(false, false, callback);
33551     }
33552 });/*
33553  * Based on:
33554  * Ext JS Library 1.1.1
33555  * Copyright(c) 2006-2007, Ext JS, LLC.
33556  *
33557  * Originally Released Under LGPL - original licence link has changed is not relivant.
33558  *
33559  * Fork - LGPL
33560  * <script type="text/javascript">
33561  */
33562  
33563 /**
33564  * @class Roo.tree.TreeNodeUI
33565  * @constructor
33566  * @param {Object} node The node to render
33567  * The TreeNode UI implementation is separate from the
33568  * tree implementation. Unless you are customizing the tree UI,
33569  * you should never have to use this directly.
33570  */
33571 Roo.tree.TreeNodeUI = function(node){
33572     this.node = node;
33573     this.rendered = false;
33574     this.animating = false;
33575     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33576 };
33577
33578 Roo.tree.TreeNodeUI.prototype = {
33579     removeChild : function(node){
33580         if(this.rendered){
33581             this.ctNode.removeChild(node.ui.getEl());
33582         }
33583     },
33584
33585     beforeLoad : function(){
33586          this.addClass("x-tree-node-loading");
33587     },
33588
33589     afterLoad : function(){
33590          this.removeClass("x-tree-node-loading");
33591     },
33592
33593     onTextChange : function(node, text, oldText){
33594         if(this.rendered){
33595             this.textNode.innerHTML = text;
33596         }
33597     },
33598
33599     onDisableChange : function(node, state){
33600         this.disabled = state;
33601         if(state){
33602             this.addClass("x-tree-node-disabled");
33603         }else{
33604             this.removeClass("x-tree-node-disabled");
33605         }
33606     },
33607
33608     onSelectedChange : function(state){
33609         if(state){
33610             this.focus();
33611             this.addClass("x-tree-selected");
33612         }else{
33613             //this.blur();
33614             this.removeClass("x-tree-selected");
33615         }
33616     },
33617
33618     onMove : function(tree, node, oldParent, newParent, index, refNode){
33619         this.childIndent = null;
33620         if(this.rendered){
33621             var targetNode = newParent.ui.getContainer();
33622             if(!targetNode){//target not rendered
33623                 this.holder = document.createElement("div");
33624                 this.holder.appendChild(this.wrap);
33625                 return;
33626             }
33627             var insertBefore = refNode ? refNode.ui.getEl() : null;
33628             if(insertBefore){
33629                 targetNode.insertBefore(this.wrap, insertBefore);
33630             }else{
33631                 targetNode.appendChild(this.wrap);
33632             }
33633             this.node.renderIndent(true);
33634         }
33635     },
33636
33637     addClass : function(cls){
33638         if(this.elNode){
33639             Roo.fly(this.elNode).addClass(cls);
33640         }
33641     },
33642
33643     removeClass : function(cls){
33644         if(this.elNode){
33645             Roo.fly(this.elNode).removeClass(cls);
33646         }
33647     },
33648
33649     remove : function(){
33650         if(this.rendered){
33651             this.holder = document.createElement("div");
33652             this.holder.appendChild(this.wrap);
33653         }
33654     },
33655
33656     fireEvent : function(){
33657         return this.node.fireEvent.apply(this.node, arguments);
33658     },
33659
33660     initEvents : function(){
33661         this.node.on("move", this.onMove, this);
33662         var E = Roo.EventManager;
33663         var a = this.anchor;
33664
33665         var el = Roo.fly(a, '_treeui');
33666
33667         if(Roo.isOpera){ // opera render bug ignores the CSS
33668             el.setStyle("text-decoration", "none");
33669         }
33670
33671         el.on("click", this.onClick, this);
33672         el.on("dblclick", this.onDblClick, this);
33673
33674         if(this.checkbox){
33675             Roo.EventManager.on(this.checkbox,
33676                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33677         }
33678
33679         el.on("contextmenu", this.onContextMenu, this);
33680
33681         var icon = Roo.fly(this.iconNode);
33682         icon.on("click", this.onClick, this);
33683         icon.on("dblclick", this.onDblClick, this);
33684         icon.on("contextmenu", this.onContextMenu, this);
33685         E.on(this.ecNode, "click", this.ecClick, this, true);
33686
33687         if(this.node.disabled){
33688             this.addClass("x-tree-node-disabled");
33689         }
33690         if(this.node.hidden){
33691             this.addClass("x-tree-node-disabled");
33692         }
33693         var ot = this.node.getOwnerTree();
33694         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33695         if(dd && (!this.node.isRoot || ot.rootVisible)){
33696             Roo.dd.Registry.register(this.elNode, {
33697                 node: this.node,
33698                 handles: this.getDDHandles(),
33699                 isHandle: false
33700             });
33701         }
33702     },
33703
33704     getDDHandles : function(){
33705         return [this.iconNode, this.textNode];
33706     },
33707
33708     hide : function(){
33709         if(this.rendered){
33710             this.wrap.style.display = "none";
33711         }
33712     },
33713
33714     show : function(){
33715         if(this.rendered){
33716             this.wrap.style.display = "";
33717         }
33718     },
33719
33720     onContextMenu : function(e){
33721         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33722             e.preventDefault();
33723             this.focus();
33724             this.fireEvent("contextmenu", this.node, e);
33725         }
33726     },
33727
33728     onClick : function(e){
33729         if(this.dropping){
33730             e.stopEvent();
33731             return;
33732         }
33733         if(this.fireEvent("beforeclick", this.node, e) !== false){
33734             if(!this.disabled && this.node.attributes.href){
33735                 this.fireEvent("click", this.node, e);
33736                 return;
33737             }
33738             e.preventDefault();
33739             if(this.disabled){
33740                 return;
33741             }
33742
33743             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33744                 this.node.toggle();
33745             }
33746
33747             this.fireEvent("click", this.node, e);
33748         }else{
33749             e.stopEvent();
33750         }
33751     },
33752
33753     onDblClick : function(e){
33754         e.preventDefault();
33755         if(this.disabled){
33756             return;
33757         }
33758         if(this.checkbox){
33759             this.toggleCheck();
33760         }
33761         if(!this.animating && this.node.hasChildNodes()){
33762             this.node.toggle();
33763         }
33764         this.fireEvent("dblclick", this.node, e);
33765     },
33766
33767     onCheckChange : function(){
33768         var checked = this.checkbox.checked;
33769         this.node.attributes.checked = checked;
33770         this.fireEvent('checkchange', this.node, checked);
33771     },
33772
33773     ecClick : function(e){
33774         if(!this.animating && this.node.hasChildNodes()){
33775             this.node.toggle();
33776         }
33777     },
33778
33779     startDrop : function(){
33780         this.dropping = true;
33781     },
33782
33783     // delayed drop so the click event doesn't get fired on a drop
33784     endDrop : function(){
33785        setTimeout(function(){
33786            this.dropping = false;
33787        }.createDelegate(this), 50);
33788     },
33789
33790     expand : function(){
33791         this.updateExpandIcon();
33792         this.ctNode.style.display = "";
33793     },
33794
33795     focus : function(){
33796         if(!this.node.preventHScroll){
33797             try{this.anchor.focus();
33798             }catch(e){}
33799         }else if(!Roo.isIE){
33800             try{
33801                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33802                 var l = noscroll.scrollLeft;
33803                 this.anchor.focus();
33804                 noscroll.scrollLeft = l;
33805             }catch(e){}
33806         }
33807     },
33808
33809     toggleCheck : function(value){
33810         var cb = this.checkbox;
33811         if(cb){
33812             cb.checked = (value === undefined ? !cb.checked : value);
33813         }
33814     },
33815
33816     blur : function(){
33817         try{
33818             this.anchor.blur();
33819         }catch(e){}
33820     },
33821
33822     animExpand : function(callback){
33823         var ct = Roo.get(this.ctNode);
33824         ct.stopFx();
33825         if(!this.node.hasChildNodes()){
33826             this.updateExpandIcon();
33827             this.ctNode.style.display = "";
33828             Roo.callback(callback);
33829             return;
33830         }
33831         this.animating = true;
33832         this.updateExpandIcon();
33833
33834         ct.slideIn('t', {
33835            callback : function(){
33836                this.animating = false;
33837                Roo.callback(callback);
33838             },
33839             scope: this,
33840             duration: this.node.ownerTree.duration || .25
33841         });
33842     },
33843
33844     highlight : function(){
33845         var tree = this.node.getOwnerTree();
33846         Roo.fly(this.wrap).highlight(
33847             tree.hlColor || "C3DAF9",
33848             {endColor: tree.hlBaseColor}
33849         );
33850     },
33851
33852     collapse : function(){
33853         this.updateExpandIcon();
33854         this.ctNode.style.display = "none";
33855     },
33856
33857     animCollapse : function(callback){
33858         var ct = Roo.get(this.ctNode);
33859         ct.enableDisplayMode('block');
33860         ct.stopFx();
33861
33862         this.animating = true;
33863         this.updateExpandIcon();
33864
33865         ct.slideOut('t', {
33866             callback : function(){
33867                this.animating = false;
33868                Roo.callback(callback);
33869             },
33870             scope: this,
33871             duration: this.node.ownerTree.duration || .25
33872         });
33873     },
33874
33875     getContainer : function(){
33876         return this.ctNode;
33877     },
33878
33879     getEl : function(){
33880         return this.wrap;
33881     },
33882
33883     appendDDGhost : function(ghostNode){
33884         ghostNode.appendChild(this.elNode.cloneNode(true));
33885     },
33886
33887     getDDRepairXY : function(){
33888         return Roo.lib.Dom.getXY(this.iconNode);
33889     },
33890
33891     onRender : function(){
33892         this.render();
33893     },
33894
33895     render : function(bulkRender){
33896         var n = this.node, a = n.attributes;
33897         var targetNode = n.parentNode ?
33898               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33899
33900         if(!this.rendered){
33901             this.rendered = true;
33902
33903             this.renderElements(n, a, targetNode, bulkRender);
33904
33905             if(a.qtip){
33906                if(this.textNode.setAttributeNS){
33907                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33908                    if(a.qtipTitle){
33909                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33910                    }
33911                }else{
33912                    this.textNode.setAttribute("ext:qtip", a.qtip);
33913                    if(a.qtipTitle){
33914                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33915                    }
33916                }
33917             }else if(a.qtipCfg){
33918                 a.qtipCfg.target = Roo.id(this.textNode);
33919                 Roo.QuickTips.register(a.qtipCfg);
33920             }
33921             this.initEvents();
33922             if(!this.node.expanded){
33923                 this.updateExpandIcon();
33924             }
33925         }else{
33926             if(bulkRender === true) {
33927                 targetNode.appendChild(this.wrap);
33928             }
33929         }
33930     },
33931
33932     renderElements : function(n, a, targetNode, bulkRender)
33933     {
33934         // add some indent caching, this helps performance when rendering a large tree
33935         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33936         var t = n.getOwnerTree();
33937         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33938         if (typeof(n.attributes.html) != 'undefined') {
33939             txt = n.attributes.html;
33940         }
33941         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33942         var cb = typeof a.checked == 'boolean';
33943         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33944         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33945             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33946             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33947             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33948             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33949             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33950              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33951                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33952             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33953             "</li>"];
33954
33955         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33956             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33957                                 n.nextSibling.ui.getEl(), buf.join(""));
33958         }else{
33959             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33960         }
33961
33962         this.elNode = this.wrap.childNodes[0];
33963         this.ctNode = this.wrap.childNodes[1];
33964         var cs = this.elNode.childNodes;
33965         this.indentNode = cs[0];
33966         this.ecNode = cs[1];
33967         this.iconNode = cs[2];
33968         var index = 3;
33969         if(cb){
33970             this.checkbox = cs[3];
33971             index++;
33972         }
33973         this.anchor = cs[index];
33974         this.textNode = cs[index].firstChild;
33975     },
33976
33977     getAnchor : function(){
33978         return this.anchor;
33979     },
33980
33981     getTextEl : function(){
33982         return this.textNode;
33983     },
33984
33985     getIconEl : function(){
33986         return this.iconNode;
33987     },
33988
33989     isChecked : function(){
33990         return this.checkbox ? this.checkbox.checked : false;
33991     },
33992
33993     updateExpandIcon : function(){
33994         if(this.rendered){
33995             var n = this.node, c1, c2;
33996             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33997             var hasChild = n.hasChildNodes();
33998             if(hasChild){
33999                 if(n.expanded){
34000                     cls += "-minus";
34001                     c1 = "x-tree-node-collapsed";
34002                     c2 = "x-tree-node-expanded";
34003                 }else{
34004                     cls += "-plus";
34005                     c1 = "x-tree-node-expanded";
34006                     c2 = "x-tree-node-collapsed";
34007                 }
34008                 if(this.wasLeaf){
34009                     this.removeClass("x-tree-node-leaf");
34010                     this.wasLeaf = false;
34011                 }
34012                 if(this.c1 != c1 || this.c2 != c2){
34013                     Roo.fly(this.elNode).replaceClass(c1, c2);
34014                     this.c1 = c1; this.c2 = c2;
34015                 }
34016             }else{
34017                 // this changes non-leafs into leafs if they have no children.
34018                 // it's not very rational behaviour..
34019                 
34020                 if(!this.wasLeaf && this.node.leaf){
34021                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34022                     delete this.c1;
34023                     delete this.c2;
34024                     this.wasLeaf = true;
34025                 }
34026             }
34027             var ecc = "x-tree-ec-icon "+cls;
34028             if(this.ecc != ecc){
34029                 this.ecNode.className = ecc;
34030                 this.ecc = ecc;
34031             }
34032         }
34033     },
34034
34035     getChildIndent : function(){
34036         if(!this.childIndent){
34037             var buf = [];
34038             var p = this.node;
34039             while(p){
34040                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34041                     if(!p.isLast()) {
34042                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34043                     } else {
34044                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34045                     }
34046                 }
34047                 p = p.parentNode;
34048             }
34049             this.childIndent = buf.join("");
34050         }
34051         return this.childIndent;
34052     },
34053
34054     renderIndent : function(){
34055         if(this.rendered){
34056             var indent = "";
34057             var p = this.node.parentNode;
34058             if(p){
34059                 indent = p.ui.getChildIndent();
34060             }
34061             if(this.indentMarkup != indent){ // don't rerender if not required
34062                 this.indentNode.innerHTML = indent;
34063                 this.indentMarkup = indent;
34064             }
34065             this.updateExpandIcon();
34066         }
34067     }
34068 };
34069
34070 Roo.tree.RootTreeNodeUI = function(){
34071     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34072 };
34073 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34074     render : function(){
34075         if(!this.rendered){
34076             var targetNode = this.node.ownerTree.innerCt.dom;
34077             this.node.expanded = true;
34078             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34079             this.wrap = this.ctNode = targetNode.firstChild;
34080         }
34081     },
34082     collapse : function(){
34083     },
34084     expand : function(){
34085     }
34086 });/*
34087  * Based on:
34088  * Ext JS Library 1.1.1
34089  * Copyright(c) 2006-2007, Ext JS, LLC.
34090  *
34091  * Originally Released Under LGPL - original licence link has changed is not relivant.
34092  *
34093  * Fork - LGPL
34094  * <script type="text/javascript">
34095  */
34096 /**
34097  * @class Roo.tree.TreeLoader
34098  * @extends Roo.util.Observable
34099  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34100  * nodes from a specified URL. The response must be a javascript Array definition
34101  * who's elements are node definition objects. eg:
34102  * <pre><code>
34103 {  success : true,
34104    data :      [
34105    
34106     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34107     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34108     ]
34109 }
34110
34111
34112 </code></pre>
34113  * <br><br>
34114  * The old style respose with just an array is still supported, but not recommended.
34115  * <br><br>
34116  *
34117  * A server request is sent, and child nodes are loaded only when a node is expanded.
34118  * The loading node's id is passed to the server under the parameter name "node" to
34119  * enable the server to produce the correct child nodes.
34120  * <br><br>
34121  * To pass extra parameters, an event handler may be attached to the "beforeload"
34122  * event, and the parameters specified in the TreeLoader's baseParams property:
34123  * <pre><code>
34124     myTreeLoader.on("beforeload", function(treeLoader, node) {
34125         this.baseParams.category = node.attributes.category;
34126     }, this);
34127 </code></pre><
34128  * This would pass an HTTP parameter called "category" to the server containing
34129  * the value of the Node's "category" attribute.
34130  * @constructor
34131  * Creates a new Treeloader.
34132  * @param {Object} config A config object containing config properties.
34133  */
34134 Roo.tree.TreeLoader = function(config){
34135     this.baseParams = {};
34136     this.requestMethod = "POST";
34137     Roo.apply(this, config);
34138
34139     this.addEvents({
34140     
34141         /**
34142          * @event beforeload
34143          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34144          * @param {Object} This TreeLoader object.
34145          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34146          * @param {Object} callback The callback function specified in the {@link #load} call.
34147          */
34148         beforeload : true,
34149         /**
34150          * @event load
34151          * Fires when the node has been successfuly loaded.
34152          * @param {Object} This TreeLoader object.
34153          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34154          * @param {Object} response The response object containing the data from the server.
34155          */
34156         load : true,
34157         /**
34158          * @event loadexception
34159          * Fires if the network request failed.
34160          * @param {Object} This TreeLoader object.
34161          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34162          * @param {Object} response The response object containing the data from the server.
34163          */
34164         loadexception : true,
34165         /**
34166          * @event create
34167          * Fires before a node is created, enabling you to return custom Node types 
34168          * @param {Object} This TreeLoader object.
34169          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34170          */
34171         create : true
34172     });
34173
34174     Roo.tree.TreeLoader.superclass.constructor.call(this);
34175 };
34176
34177 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34178     /**
34179     * @cfg {String} dataUrl The URL from which to request a Json string which
34180     * specifies an array of node definition object representing the child nodes
34181     * to be loaded.
34182     */
34183     /**
34184     * @cfg {String} requestMethod either GET or POST
34185     * defaults to POST (due to BC)
34186     * to be loaded.
34187     */
34188     /**
34189     * @cfg {Object} baseParams (optional) An object containing properties which
34190     * specify HTTP parameters to be passed to each request for child nodes.
34191     */
34192     /**
34193     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34194     * created by this loader. If the attributes sent by the server have an attribute in this object,
34195     * they take priority.
34196     */
34197     /**
34198     * @cfg {Object} uiProviders (optional) An object containing properties which
34199     * 
34200     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34201     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34202     * <i>uiProvider</i> attribute of a returned child node is a string rather
34203     * than a reference to a TreeNodeUI implementation, this that string value
34204     * is used as a property name in the uiProviders object. You can define the provider named
34205     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34206     */
34207     uiProviders : {},
34208
34209     /**
34210     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34211     * child nodes before loading.
34212     */
34213     clearOnLoad : true,
34214
34215     /**
34216     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34217     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34218     * Grid query { data : [ .....] }
34219     */
34220     
34221     root : false,
34222      /**
34223     * @cfg {String} queryParam (optional) 
34224     * Name of the query as it will be passed on the querystring (defaults to 'node')
34225     * eg. the request will be ?node=[id]
34226     */
34227     
34228     
34229     queryParam: false,
34230     
34231     /**
34232      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34233      * This is called automatically when a node is expanded, but may be used to reload
34234      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34235      * @param {Roo.tree.TreeNode} node
34236      * @param {Function} callback
34237      */
34238     load : function(node, callback){
34239         if(this.clearOnLoad){
34240             while(node.firstChild){
34241                 node.removeChild(node.firstChild);
34242             }
34243         }
34244         if(node.attributes.children){ // preloaded json children
34245             var cs = node.attributes.children;
34246             for(var i = 0, len = cs.length; i < len; i++){
34247                 node.appendChild(this.createNode(cs[i]));
34248             }
34249             if(typeof callback == "function"){
34250                 callback();
34251             }
34252         }else if(this.dataUrl){
34253             this.requestData(node, callback);
34254         }
34255     },
34256
34257     getParams: function(node){
34258         var buf = [], bp = this.baseParams;
34259         for(var key in bp){
34260             if(typeof bp[key] != "function"){
34261                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34262             }
34263         }
34264         var n = this.queryParam === false ? 'node' : this.queryParam;
34265         buf.push(n + "=", encodeURIComponent(node.id));
34266         return buf.join("");
34267     },
34268
34269     requestData : function(node, callback){
34270         if(this.fireEvent("beforeload", this, node, callback) !== false){
34271             this.transId = Roo.Ajax.request({
34272                 method:this.requestMethod,
34273                 url: this.dataUrl||this.url,
34274                 success: this.handleResponse,
34275                 failure: this.handleFailure,
34276                 scope: this,
34277                 argument: {callback: callback, node: node},
34278                 params: this.getParams(node)
34279             });
34280         }else{
34281             // if the load is cancelled, make sure we notify
34282             // the node that we are done
34283             if(typeof callback == "function"){
34284                 callback();
34285             }
34286         }
34287     },
34288
34289     isLoading : function(){
34290         return this.transId ? true : false;
34291     },
34292
34293     abort : function(){
34294         if(this.isLoading()){
34295             Roo.Ajax.abort(this.transId);
34296         }
34297     },
34298
34299     // private
34300     createNode : function(attr)
34301     {
34302         // apply baseAttrs, nice idea Corey!
34303         if(this.baseAttrs){
34304             Roo.applyIf(attr, this.baseAttrs);
34305         }
34306         if(this.applyLoader !== false){
34307             attr.loader = this;
34308         }
34309         // uiProvider = depreciated..
34310         
34311         if(typeof(attr.uiProvider) == 'string'){
34312            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34313                 /**  eval:var:attr */ eval(attr.uiProvider);
34314         }
34315         if(typeof(this.uiProviders['default']) != 'undefined') {
34316             attr.uiProvider = this.uiProviders['default'];
34317         }
34318         
34319         this.fireEvent('create', this, attr);
34320         
34321         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34322         return(attr.leaf ?
34323                         new Roo.tree.TreeNode(attr) :
34324                         new Roo.tree.AsyncTreeNode(attr));
34325     },
34326
34327     processResponse : function(response, node, callback)
34328     {
34329         var json = response.responseText;
34330         try {
34331             
34332             var o = Roo.decode(json);
34333             
34334             if (this.root === false && typeof(o.success) != undefined) {
34335                 this.root = 'data'; // the default behaviour for list like data..
34336                 }
34337                 
34338             if (this.root !== false &&  !o.success) {
34339                 // it's a failure condition.
34340                 var a = response.argument;
34341                 this.fireEvent("loadexception", this, a.node, response);
34342                 Roo.log("Load failed - should have a handler really");
34343                 return;
34344             }
34345             
34346             
34347             
34348             if (this.root !== false) {
34349                  o = o[this.root];
34350             }
34351             
34352             for(var i = 0, len = o.length; i < len; i++){
34353                 var n = this.createNode(o[i]);
34354                 if(n){
34355                     node.appendChild(n);
34356                 }
34357             }
34358             if(typeof callback == "function"){
34359                 callback(this, node);
34360             }
34361         }catch(e){
34362             this.handleFailure(response);
34363         }
34364     },
34365
34366     handleResponse : function(response){
34367         this.transId = false;
34368         var a = response.argument;
34369         this.processResponse(response, a.node, a.callback);
34370         this.fireEvent("load", this, a.node, response);
34371     },
34372
34373     handleFailure : function(response)
34374     {
34375         // should handle failure better..
34376         this.transId = false;
34377         var a = response.argument;
34378         this.fireEvent("loadexception", this, a.node, response);
34379         if(typeof a.callback == "function"){
34380             a.callback(this, a.node);
34381         }
34382     }
34383 });/*
34384  * Based on:
34385  * Ext JS Library 1.1.1
34386  * Copyright(c) 2006-2007, Ext JS, LLC.
34387  *
34388  * Originally Released Under LGPL - original licence link has changed is not relivant.
34389  *
34390  * Fork - LGPL
34391  * <script type="text/javascript">
34392  */
34393
34394 /**
34395 * @class Roo.tree.TreeFilter
34396 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34397 * @param {TreePanel} tree
34398 * @param {Object} config (optional)
34399  */
34400 Roo.tree.TreeFilter = function(tree, config){
34401     this.tree = tree;
34402     this.filtered = {};
34403     Roo.apply(this, config);
34404 };
34405
34406 Roo.tree.TreeFilter.prototype = {
34407     clearBlank:false,
34408     reverse:false,
34409     autoClear:false,
34410     remove:false,
34411
34412      /**
34413      * Filter the data by a specific attribute.
34414      * @param {String/RegExp} value Either string that the attribute value
34415      * should start with or a RegExp to test against the attribute
34416      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34417      * @param {TreeNode} startNode (optional) The node to start the filter at.
34418      */
34419     filter : function(value, attr, startNode){
34420         attr = attr || "text";
34421         var f;
34422         if(typeof value == "string"){
34423             var vlen = value.length;
34424             // auto clear empty filter
34425             if(vlen == 0 && this.clearBlank){
34426                 this.clear();
34427                 return;
34428             }
34429             value = value.toLowerCase();
34430             f = function(n){
34431                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34432             };
34433         }else if(value.exec){ // regex?
34434             f = function(n){
34435                 return value.test(n.attributes[attr]);
34436             };
34437         }else{
34438             throw 'Illegal filter type, must be string or regex';
34439         }
34440         this.filterBy(f, null, startNode);
34441         },
34442
34443     /**
34444      * Filter by a function. The passed function will be called with each
34445      * node in the tree (or from the startNode). If the function returns true, the node is kept
34446      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34447      * @param {Function} fn The filter function
34448      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34449      */
34450     filterBy : function(fn, scope, startNode){
34451         startNode = startNode || this.tree.root;
34452         if(this.autoClear){
34453             this.clear();
34454         }
34455         var af = this.filtered, rv = this.reverse;
34456         var f = function(n){
34457             if(n == startNode){
34458                 return true;
34459             }
34460             if(af[n.id]){
34461                 return false;
34462             }
34463             var m = fn.call(scope || n, n);
34464             if(!m || rv){
34465                 af[n.id] = n;
34466                 n.ui.hide();
34467                 return false;
34468             }
34469             return true;
34470         };
34471         startNode.cascade(f);
34472         if(this.remove){
34473            for(var id in af){
34474                if(typeof id != "function"){
34475                    var n = af[id];
34476                    if(n && n.parentNode){
34477                        n.parentNode.removeChild(n);
34478                    }
34479                }
34480            }
34481         }
34482     },
34483
34484     /**
34485      * Clears the current filter. Note: with the "remove" option
34486      * set a filter cannot be cleared.
34487      */
34488     clear : function(){
34489         var t = this.tree;
34490         var af = this.filtered;
34491         for(var id in af){
34492             if(typeof id != "function"){
34493                 var n = af[id];
34494                 if(n){
34495                     n.ui.show();
34496                 }
34497             }
34498         }
34499         this.filtered = {};
34500     }
34501 };
34502 /*
34503  * Based on:
34504  * Ext JS Library 1.1.1
34505  * Copyright(c) 2006-2007, Ext JS, LLC.
34506  *
34507  * Originally Released Under LGPL - original licence link has changed is not relivant.
34508  *
34509  * Fork - LGPL
34510  * <script type="text/javascript">
34511  */
34512  
34513
34514 /**
34515  * @class Roo.tree.TreeSorter
34516  * Provides sorting of nodes in a TreePanel
34517  * 
34518  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34519  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34520  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34521  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34522  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34523  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34524  * @constructor
34525  * @param {TreePanel} tree
34526  * @param {Object} config
34527  */
34528 Roo.tree.TreeSorter = function(tree, config){
34529     Roo.apply(this, config);
34530     tree.on("beforechildrenrendered", this.doSort, this);
34531     tree.on("append", this.updateSort, this);
34532     tree.on("insert", this.updateSort, this);
34533     
34534     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34535     var p = this.property || "text";
34536     var sortType = this.sortType;
34537     var fs = this.folderSort;
34538     var cs = this.caseSensitive === true;
34539     var leafAttr = this.leafAttr || 'leaf';
34540
34541     this.sortFn = function(n1, n2){
34542         if(fs){
34543             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34544                 return 1;
34545             }
34546             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34547                 return -1;
34548             }
34549         }
34550         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34551         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34552         if(v1 < v2){
34553                         return dsc ? +1 : -1;
34554                 }else if(v1 > v2){
34555                         return dsc ? -1 : +1;
34556         }else{
34557                 return 0;
34558         }
34559     };
34560 };
34561
34562 Roo.tree.TreeSorter.prototype = {
34563     doSort : function(node){
34564         node.sort(this.sortFn);
34565     },
34566     
34567     compareNodes : function(n1, n2){
34568         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34569     },
34570     
34571     updateSort : function(tree, node){
34572         if(node.childrenRendered){
34573             this.doSort.defer(1, this, [node]);
34574         }
34575     }
34576 };/*
34577  * Based on:
34578  * Ext JS Library 1.1.1
34579  * Copyright(c) 2006-2007, Ext JS, LLC.
34580  *
34581  * Originally Released Under LGPL - original licence link has changed is not relivant.
34582  *
34583  * Fork - LGPL
34584  * <script type="text/javascript">
34585  */
34586
34587 if(Roo.dd.DropZone){
34588     
34589 Roo.tree.TreeDropZone = function(tree, config){
34590     this.allowParentInsert = false;
34591     this.allowContainerDrop = false;
34592     this.appendOnly = false;
34593     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34594     this.tree = tree;
34595     this.lastInsertClass = "x-tree-no-status";
34596     this.dragOverData = {};
34597 };
34598
34599 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34600     ddGroup : "TreeDD",
34601     scroll:  true,
34602     
34603     expandDelay : 1000,
34604     
34605     expandNode : function(node){
34606         if(node.hasChildNodes() && !node.isExpanded()){
34607             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34608         }
34609     },
34610     
34611     queueExpand : function(node){
34612         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34613     },
34614     
34615     cancelExpand : function(){
34616         if(this.expandProcId){
34617             clearTimeout(this.expandProcId);
34618             this.expandProcId = false;
34619         }
34620     },
34621     
34622     isValidDropPoint : function(n, pt, dd, e, data){
34623         if(!n || !data){ return false; }
34624         var targetNode = n.node;
34625         var dropNode = data.node;
34626         // default drop rules
34627         if(!(targetNode && targetNode.isTarget && pt)){
34628             return false;
34629         }
34630         if(pt == "append" && targetNode.allowChildren === false){
34631             return false;
34632         }
34633         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34634             return false;
34635         }
34636         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34637             return false;
34638         }
34639         // reuse the object
34640         var overEvent = this.dragOverData;
34641         overEvent.tree = this.tree;
34642         overEvent.target = targetNode;
34643         overEvent.data = data;
34644         overEvent.point = pt;
34645         overEvent.source = dd;
34646         overEvent.rawEvent = e;
34647         overEvent.dropNode = dropNode;
34648         overEvent.cancel = false;  
34649         var result = this.tree.fireEvent("nodedragover", overEvent);
34650         return overEvent.cancel === false && result !== false;
34651     },
34652     
34653     getDropPoint : function(e, n, dd)
34654     {
34655         var tn = n.node;
34656         if(tn.isRoot){
34657             return tn.allowChildren !== false ? "append" : false; // always append for root
34658         }
34659         var dragEl = n.ddel;
34660         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34661         var y = Roo.lib.Event.getPageY(e);
34662         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34663         
34664         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34665         var noAppend = tn.allowChildren === false;
34666         if(this.appendOnly || tn.parentNode.allowChildren === false){
34667             return noAppend ? false : "append";
34668         }
34669         var noBelow = false;
34670         if(!this.allowParentInsert){
34671             noBelow = tn.hasChildNodes() && tn.isExpanded();
34672         }
34673         var q = (b - t) / (noAppend ? 2 : 3);
34674         if(y >= t && y < (t + q)){
34675             return "above";
34676         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34677             return "below";
34678         }else{
34679             return "append";
34680         }
34681     },
34682     
34683     onNodeEnter : function(n, dd, e, data)
34684     {
34685         this.cancelExpand();
34686     },
34687     
34688     onNodeOver : function(n, dd, e, data)
34689     {
34690        
34691         var pt = this.getDropPoint(e, n, dd);
34692         var node = n.node;
34693         
34694         // auto node expand check
34695         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34696             this.queueExpand(node);
34697         }else if(pt != "append"){
34698             this.cancelExpand();
34699         }
34700         
34701         // set the insert point style on the target node
34702         var returnCls = this.dropNotAllowed;
34703         if(this.isValidDropPoint(n, pt, dd, e, data)){
34704            if(pt){
34705                var el = n.ddel;
34706                var cls;
34707                if(pt == "above"){
34708                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34709                    cls = "x-tree-drag-insert-above";
34710                }else if(pt == "below"){
34711                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34712                    cls = "x-tree-drag-insert-below";
34713                }else{
34714                    returnCls = "x-tree-drop-ok-append";
34715                    cls = "x-tree-drag-append";
34716                }
34717                if(this.lastInsertClass != cls){
34718                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34719                    this.lastInsertClass = cls;
34720                }
34721            }
34722        }
34723        return returnCls;
34724     },
34725     
34726     onNodeOut : function(n, dd, e, data){
34727         
34728         this.cancelExpand();
34729         this.removeDropIndicators(n);
34730     },
34731     
34732     onNodeDrop : function(n, dd, e, data){
34733         var point = this.getDropPoint(e, n, dd);
34734         var targetNode = n.node;
34735         targetNode.ui.startDrop();
34736         if(!this.isValidDropPoint(n, point, dd, e, data)){
34737             targetNode.ui.endDrop();
34738             return false;
34739         }
34740         // first try to find the drop node
34741         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34742         var dropEvent = {
34743             tree : this.tree,
34744             target: targetNode,
34745             data: data,
34746             point: point,
34747             source: dd,
34748             rawEvent: e,
34749             dropNode: dropNode,
34750             cancel: !dropNode   
34751         };
34752         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34753         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34754             targetNode.ui.endDrop();
34755             return false;
34756         }
34757         // allow target changing
34758         targetNode = dropEvent.target;
34759         if(point == "append" && !targetNode.isExpanded()){
34760             targetNode.expand(false, null, function(){
34761                 this.completeDrop(dropEvent);
34762             }.createDelegate(this));
34763         }else{
34764             this.completeDrop(dropEvent);
34765         }
34766         return true;
34767     },
34768     
34769     completeDrop : function(de){
34770         var ns = de.dropNode, p = de.point, t = de.target;
34771         if(!(ns instanceof Array)){
34772             ns = [ns];
34773         }
34774         var n;
34775         for(var i = 0, len = ns.length; i < len; i++){
34776             n = ns[i];
34777             if(p == "above"){
34778                 t.parentNode.insertBefore(n, t);
34779             }else if(p == "below"){
34780                 t.parentNode.insertBefore(n, t.nextSibling);
34781             }else{
34782                 t.appendChild(n);
34783             }
34784         }
34785         n.ui.focus();
34786         if(this.tree.hlDrop){
34787             n.ui.highlight();
34788         }
34789         t.ui.endDrop();
34790         this.tree.fireEvent("nodedrop", de);
34791     },
34792     
34793     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34794         if(this.tree.hlDrop){
34795             dropNode.ui.focus();
34796             dropNode.ui.highlight();
34797         }
34798         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34799     },
34800     
34801     getTree : function(){
34802         return this.tree;
34803     },
34804     
34805     removeDropIndicators : function(n){
34806         if(n && n.ddel){
34807             var el = n.ddel;
34808             Roo.fly(el).removeClass([
34809                     "x-tree-drag-insert-above",
34810                     "x-tree-drag-insert-below",
34811                     "x-tree-drag-append"]);
34812             this.lastInsertClass = "_noclass";
34813         }
34814     },
34815     
34816     beforeDragDrop : function(target, e, id){
34817         this.cancelExpand();
34818         return true;
34819     },
34820     
34821     afterRepair : function(data){
34822         if(data && Roo.enableFx){
34823             data.node.ui.highlight();
34824         }
34825         this.hideProxy();
34826     } 
34827     
34828 });
34829
34830 }
34831 /*
34832  * Based on:
34833  * Ext JS Library 1.1.1
34834  * Copyright(c) 2006-2007, Ext JS, LLC.
34835  *
34836  * Originally Released Under LGPL - original licence link has changed is not relivant.
34837  *
34838  * Fork - LGPL
34839  * <script type="text/javascript">
34840  */
34841  
34842
34843 if(Roo.dd.DragZone){
34844 Roo.tree.TreeDragZone = function(tree, config){
34845     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34846     this.tree = tree;
34847 };
34848
34849 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34850     ddGroup : "TreeDD",
34851    
34852     onBeforeDrag : function(data, e){
34853         var n = data.node;
34854         return n && n.draggable && !n.disabled;
34855     },
34856      
34857     
34858     onInitDrag : function(e){
34859         var data = this.dragData;
34860         this.tree.getSelectionModel().select(data.node);
34861         this.proxy.update("");
34862         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34863         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34864     },
34865     
34866     getRepairXY : function(e, data){
34867         return data.node.ui.getDDRepairXY();
34868     },
34869     
34870     onEndDrag : function(data, e){
34871         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34872         
34873         
34874     },
34875     
34876     onValidDrop : function(dd, e, id){
34877         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34878         this.hideProxy();
34879     },
34880     
34881     beforeInvalidDrop : function(e, id){
34882         // this scrolls the original position back into view
34883         var sm = this.tree.getSelectionModel();
34884         sm.clearSelections();
34885         sm.select(this.dragData.node);
34886     }
34887 });
34888 }/*
34889  * Based on:
34890  * Ext JS Library 1.1.1
34891  * Copyright(c) 2006-2007, Ext JS, LLC.
34892  *
34893  * Originally Released Under LGPL - original licence link has changed is not relivant.
34894  *
34895  * Fork - LGPL
34896  * <script type="text/javascript">
34897  */
34898 /**
34899  * @class Roo.tree.TreeEditor
34900  * @extends Roo.Editor
34901  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34902  * as the editor field.
34903  * @constructor
34904  * @param {Object} config (used to be the tree panel.)
34905  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34906  * 
34907  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34908  * @cfg {Roo.form.TextField|Object} field The field configuration
34909  *
34910  * 
34911  */
34912 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34913     var tree = config;
34914     var field;
34915     if (oldconfig) { // old style..
34916         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34917     } else {
34918         // new style..
34919         tree = config.tree;
34920         config.field = config.field  || {};
34921         config.field.xtype = 'TextField';
34922         field = Roo.factory(config.field, Roo.form);
34923     }
34924     config = config || {};
34925     
34926     
34927     this.addEvents({
34928         /**
34929          * @event beforenodeedit
34930          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34931          * false from the handler of this event.
34932          * @param {Editor} this
34933          * @param {Roo.tree.Node} node 
34934          */
34935         "beforenodeedit" : true
34936     });
34937     
34938     //Roo.log(config);
34939     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34940
34941     this.tree = tree;
34942
34943     tree.on('beforeclick', this.beforeNodeClick, this);
34944     tree.getTreeEl().on('mousedown', this.hide, this);
34945     this.on('complete', this.updateNode, this);
34946     this.on('beforestartedit', this.fitToTree, this);
34947     this.on('startedit', this.bindScroll, this, {delay:10});
34948     this.on('specialkey', this.onSpecialKey, this);
34949 };
34950
34951 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34952     /**
34953      * @cfg {String} alignment
34954      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34955      */
34956     alignment: "l-l",
34957     // inherit
34958     autoSize: false,
34959     /**
34960      * @cfg {Boolean} hideEl
34961      * True to hide the bound element while the editor is displayed (defaults to false)
34962      */
34963     hideEl : false,
34964     /**
34965      * @cfg {String} cls
34966      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34967      */
34968     cls: "x-small-editor x-tree-editor",
34969     /**
34970      * @cfg {Boolean} shim
34971      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34972      */
34973     shim:false,
34974     // inherit
34975     shadow:"frame",
34976     /**
34977      * @cfg {Number} maxWidth
34978      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34979      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34980      * scroll and client offsets into account prior to each edit.
34981      */
34982     maxWidth: 250,
34983
34984     editDelay : 350,
34985
34986     // private
34987     fitToTree : function(ed, el){
34988         var td = this.tree.getTreeEl().dom, nd = el.dom;
34989         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34990             td.scrollLeft = nd.offsetLeft;
34991         }
34992         var w = Math.min(
34993                 this.maxWidth,
34994                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34995         this.setSize(w, '');
34996         
34997         return this.fireEvent('beforenodeedit', this, this.editNode);
34998         
34999     },
35000
35001     // private
35002     triggerEdit : function(node){
35003         this.completeEdit();
35004         this.editNode = node;
35005         this.startEdit(node.ui.textNode, node.text);
35006     },
35007
35008     // private
35009     bindScroll : function(){
35010         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35011     },
35012
35013     // private
35014     beforeNodeClick : function(node, e){
35015         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35016         this.lastClick = new Date();
35017         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35018             e.stopEvent();
35019             this.triggerEdit(node);
35020             return false;
35021         }
35022         return true;
35023     },
35024
35025     // private
35026     updateNode : function(ed, value){
35027         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35028         this.editNode.setText(value);
35029     },
35030
35031     // private
35032     onHide : function(){
35033         Roo.tree.TreeEditor.superclass.onHide.call(this);
35034         if(this.editNode){
35035             this.editNode.ui.focus();
35036         }
35037     },
35038
35039     // private
35040     onSpecialKey : function(field, e){
35041         var k = e.getKey();
35042         if(k == e.ESC){
35043             e.stopEvent();
35044             this.cancelEdit();
35045         }else if(k == e.ENTER && !e.hasModifier()){
35046             e.stopEvent();
35047             this.completeEdit();
35048         }
35049     }
35050 });//<Script type="text/javascript">
35051 /*
35052  * Based on:
35053  * Ext JS Library 1.1.1
35054  * Copyright(c) 2006-2007, Ext JS, LLC.
35055  *
35056  * Originally Released Under LGPL - original licence link has changed is not relivant.
35057  *
35058  * Fork - LGPL
35059  * <script type="text/javascript">
35060  */
35061  
35062 /**
35063  * Not documented??? - probably should be...
35064  */
35065
35066 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35067     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35068     
35069     renderElements : function(n, a, targetNode, bulkRender){
35070         //consel.log("renderElements?");
35071         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35072
35073         var t = n.getOwnerTree();
35074         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35075         
35076         var cols = t.columns;
35077         var bw = t.borderWidth;
35078         var c = cols[0];
35079         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35080          var cb = typeof a.checked == "boolean";
35081         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35082         var colcls = 'x-t-' + tid + '-c0';
35083         var buf = [
35084             '<li class="x-tree-node">',
35085             
35086                 
35087                 '<div class="x-tree-node-el ', a.cls,'">',
35088                     // extran...
35089                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35090                 
35091                 
35092                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35093                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35094                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35095                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35096                            (a.iconCls ? ' '+a.iconCls : ''),
35097                            '" unselectable="on" />',
35098                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35099                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35100                              
35101                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35102                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35103                             '<span unselectable="on" qtip="' + tx + '">',
35104                              tx,
35105                              '</span></a>' ,
35106                     '</div>',
35107                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35108                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35109                  ];
35110         for(var i = 1, len = cols.length; i < len; i++){
35111             c = cols[i];
35112             colcls = 'x-t-' + tid + '-c' +i;
35113             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35114             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35115                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35116                       "</div>");
35117          }
35118          
35119          buf.push(
35120             '</a>',
35121             '<div class="x-clear"></div></div>',
35122             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35123             "</li>");
35124         
35125         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35126             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35127                                 n.nextSibling.ui.getEl(), buf.join(""));
35128         }else{
35129             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35130         }
35131         var el = this.wrap.firstChild;
35132         this.elRow = el;
35133         this.elNode = el.firstChild;
35134         this.ranchor = el.childNodes[1];
35135         this.ctNode = this.wrap.childNodes[1];
35136         var cs = el.firstChild.childNodes;
35137         this.indentNode = cs[0];
35138         this.ecNode = cs[1];
35139         this.iconNode = cs[2];
35140         var index = 3;
35141         if(cb){
35142             this.checkbox = cs[3];
35143             index++;
35144         }
35145         this.anchor = cs[index];
35146         
35147         this.textNode = cs[index].firstChild;
35148         
35149         //el.on("click", this.onClick, this);
35150         //el.on("dblclick", this.onDblClick, this);
35151         
35152         
35153        // console.log(this);
35154     },
35155     initEvents : function(){
35156         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35157         
35158             
35159         var a = this.ranchor;
35160
35161         var el = Roo.get(a);
35162
35163         if(Roo.isOpera){ // opera render bug ignores the CSS
35164             el.setStyle("text-decoration", "none");
35165         }
35166
35167         el.on("click", this.onClick, this);
35168         el.on("dblclick", this.onDblClick, this);
35169         el.on("contextmenu", this.onContextMenu, this);
35170         
35171     },
35172     
35173     /*onSelectedChange : function(state){
35174         if(state){
35175             this.focus();
35176             this.addClass("x-tree-selected");
35177         }else{
35178             //this.blur();
35179             this.removeClass("x-tree-selected");
35180         }
35181     },*/
35182     addClass : function(cls){
35183         if(this.elRow){
35184             Roo.fly(this.elRow).addClass(cls);
35185         }
35186         
35187     },
35188     
35189     
35190     removeClass : function(cls){
35191         if(this.elRow){
35192             Roo.fly(this.elRow).removeClass(cls);
35193         }
35194     }
35195
35196     
35197     
35198 });//<Script type="text/javascript">
35199
35200 /*
35201  * Based on:
35202  * Ext JS Library 1.1.1
35203  * Copyright(c) 2006-2007, Ext JS, LLC.
35204  *
35205  * Originally Released Under LGPL - original licence link has changed is not relivant.
35206  *
35207  * Fork - LGPL
35208  * <script type="text/javascript">
35209  */
35210  
35211
35212 /**
35213  * @class Roo.tree.ColumnTree
35214  * @extends Roo.data.TreePanel
35215  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35216  * @cfg {int} borderWidth  compined right/left border allowance
35217  * @constructor
35218  * @param {String/HTMLElement/Element} el The container element
35219  * @param {Object} config
35220  */
35221 Roo.tree.ColumnTree =  function(el, config)
35222 {
35223    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35224    this.addEvents({
35225         /**
35226         * @event resize
35227         * Fire this event on a container when it resizes
35228         * @param {int} w Width
35229         * @param {int} h Height
35230         */
35231        "resize" : true
35232     });
35233     this.on('resize', this.onResize, this);
35234 };
35235
35236 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35237     //lines:false,
35238     
35239     
35240     borderWidth: Roo.isBorderBox ? 0 : 2, 
35241     headEls : false,
35242     
35243     render : function(){
35244         // add the header.....
35245        
35246         Roo.tree.ColumnTree.superclass.render.apply(this);
35247         
35248         this.el.addClass('x-column-tree');
35249         
35250         this.headers = this.el.createChild(
35251             {cls:'x-tree-headers'},this.innerCt.dom);
35252    
35253         var cols = this.columns, c;
35254         var totalWidth = 0;
35255         this.headEls = [];
35256         var  len = cols.length;
35257         for(var i = 0; i < len; i++){
35258              c = cols[i];
35259              totalWidth += c.width;
35260             this.headEls.push(this.headers.createChild({
35261                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35262                  cn: {
35263                      cls:'x-tree-hd-text',
35264                      html: c.header
35265                  },
35266                  style:'width:'+(c.width-this.borderWidth)+'px;'
35267              }));
35268         }
35269         this.headers.createChild({cls:'x-clear'});
35270         // prevent floats from wrapping when clipped
35271         this.headers.setWidth(totalWidth);
35272         //this.innerCt.setWidth(totalWidth);
35273         this.innerCt.setStyle({ overflow: 'auto' });
35274         this.onResize(this.width, this.height);
35275              
35276         
35277     },
35278     onResize : function(w,h)
35279     {
35280         this.height = h;
35281         this.width = w;
35282         // resize cols..
35283         this.innerCt.setWidth(this.width);
35284         this.innerCt.setHeight(this.height-20);
35285         
35286         // headers...
35287         var cols = this.columns, c;
35288         var totalWidth = 0;
35289         var expEl = false;
35290         var len = cols.length;
35291         for(var i = 0; i < len; i++){
35292             c = cols[i];
35293             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35294                 // it's the expander..
35295                 expEl  = this.headEls[i];
35296                 continue;
35297             }
35298             totalWidth += c.width;
35299             
35300         }
35301         if (expEl) {
35302             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35303         }
35304         this.headers.setWidth(w-20);
35305
35306         
35307         
35308         
35309     }
35310 });
35311 /*
35312  * Based on:
35313  * Ext JS Library 1.1.1
35314  * Copyright(c) 2006-2007, Ext JS, LLC.
35315  *
35316  * Originally Released Under LGPL - original licence link has changed is not relivant.
35317  *
35318  * Fork - LGPL
35319  * <script type="text/javascript">
35320  */
35321  
35322 /**
35323  * @class Roo.menu.Menu
35324  * @extends Roo.util.Observable
35325  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35326  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35327  * @constructor
35328  * Creates a new Menu
35329  * @param {Object} config Configuration options
35330  */
35331 Roo.menu.Menu = function(config){
35332     Roo.apply(this, config);
35333     this.id = this.id || Roo.id();
35334     this.addEvents({
35335         /**
35336          * @event beforeshow
35337          * Fires before this menu is displayed
35338          * @param {Roo.menu.Menu} this
35339          */
35340         beforeshow : true,
35341         /**
35342          * @event beforehide
35343          * Fires before this menu is hidden
35344          * @param {Roo.menu.Menu} this
35345          */
35346         beforehide : true,
35347         /**
35348          * @event show
35349          * Fires after this menu is displayed
35350          * @param {Roo.menu.Menu} this
35351          */
35352         show : true,
35353         /**
35354          * @event hide
35355          * Fires after this menu is hidden
35356          * @param {Roo.menu.Menu} this
35357          */
35358         hide : true,
35359         /**
35360          * @event click
35361          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35362          * @param {Roo.menu.Menu} this
35363          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35364          * @param {Roo.EventObject} e
35365          */
35366         click : true,
35367         /**
35368          * @event mouseover
35369          * Fires when the mouse is hovering over this menu
35370          * @param {Roo.menu.Menu} this
35371          * @param {Roo.EventObject} e
35372          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35373          */
35374         mouseover : true,
35375         /**
35376          * @event mouseout
35377          * Fires when the mouse exits this menu
35378          * @param {Roo.menu.Menu} this
35379          * @param {Roo.EventObject} e
35380          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35381          */
35382         mouseout : true,
35383         /**
35384          * @event itemclick
35385          * Fires when a menu item contained in this menu is clicked
35386          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35387          * @param {Roo.EventObject} e
35388          */
35389         itemclick: true
35390     });
35391     if (this.registerMenu) {
35392         Roo.menu.MenuMgr.register(this);
35393     }
35394     
35395     var mis = this.items;
35396     this.items = new Roo.util.MixedCollection();
35397     if(mis){
35398         this.add.apply(this, mis);
35399     }
35400 };
35401
35402 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35403     /**
35404      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35405      */
35406     minWidth : 120,
35407     /**
35408      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35409      * for bottom-right shadow (defaults to "sides")
35410      */
35411     shadow : "sides",
35412     /**
35413      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35414      * this menu (defaults to "tl-tr?")
35415      */
35416     subMenuAlign : "tl-tr?",
35417     /**
35418      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35419      * relative to its element of origin (defaults to "tl-bl?")
35420      */
35421     defaultAlign : "tl-bl?",
35422     /**
35423      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35424      */
35425     allowOtherMenus : false,
35426     /**
35427      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35428      */
35429     registerMenu : true,
35430
35431     hidden:true,
35432
35433     // private
35434     render : function(){
35435         if(this.el){
35436             return;
35437         }
35438         var el = this.el = new Roo.Layer({
35439             cls: "x-menu",
35440             shadow:this.shadow,
35441             constrain: false,
35442             parentEl: this.parentEl || document.body,
35443             zindex:15000
35444         });
35445
35446         this.keyNav = new Roo.menu.MenuNav(this);
35447
35448         if(this.plain){
35449             el.addClass("x-menu-plain");
35450         }
35451         if(this.cls){
35452             el.addClass(this.cls);
35453         }
35454         // generic focus element
35455         this.focusEl = el.createChild({
35456             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35457         });
35458         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35459         ul.on("click", this.onClick, this);
35460         ul.on("mouseover", this.onMouseOver, this);
35461         ul.on("mouseout", this.onMouseOut, this);
35462         this.items.each(function(item){
35463             if (item.hidden) {
35464                 return;
35465             }
35466             
35467             var li = document.createElement("li");
35468             li.className = "x-menu-list-item";
35469             ul.dom.appendChild(li);
35470             item.render(li, this);
35471         }, this);
35472         this.ul = ul;
35473         this.autoWidth();
35474     },
35475
35476     // private
35477     autoWidth : function(){
35478         var el = this.el, ul = this.ul;
35479         if(!el){
35480             return;
35481         }
35482         var w = this.width;
35483         if(w){
35484             el.setWidth(w);
35485         }else if(Roo.isIE){
35486             el.setWidth(this.minWidth);
35487             var t = el.dom.offsetWidth; // force recalc
35488             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35489         }
35490     },
35491
35492     // private
35493     delayAutoWidth : function(){
35494         if(this.rendered){
35495             if(!this.awTask){
35496                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35497             }
35498             this.awTask.delay(20);
35499         }
35500     },
35501
35502     // private
35503     findTargetItem : function(e){
35504         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35505         if(t && t.menuItemId){
35506             return this.items.get(t.menuItemId);
35507         }
35508     },
35509
35510     // private
35511     onClick : function(e){
35512         var t;
35513         if(t = this.findTargetItem(e)){
35514             t.onClick(e);
35515             this.fireEvent("click", this, t, e);
35516         }
35517     },
35518
35519     // private
35520     setActiveItem : function(item, autoExpand){
35521         if(item != this.activeItem){
35522             if(this.activeItem){
35523                 this.activeItem.deactivate();
35524             }
35525             this.activeItem = item;
35526             item.activate(autoExpand);
35527         }else if(autoExpand){
35528             item.expandMenu();
35529         }
35530     },
35531
35532     // private
35533     tryActivate : function(start, step){
35534         var items = this.items;
35535         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35536             var item = items.get(i);
35537             if(!item.disabled && item.canActivate){
35538                 this.setActiveItem(item, false);
35539                 return item;
35540             }
35541         }
35542         return false;
35543     },
35544
35545     // private
35546     onMouseOver : function(e){
35547         var t;
35548         if(t = this.findTargetItem(e)){
35549             if(t.canActivate && !t.disabled){
35550                 this.setActiveItem(t, true);
35551             }
35552         }
35553         this.fireEvent("mouseover", this, e, t);
35554     },
35555
35556     // private
35557     onMouseOut : function(e){
35558         var t;
35559         if(t = this.findTargetItem(e)){
35560             if(t == this.activeItem && t.shouldDeactivate(e)){
35561                 this.activeItem.deactivate();
35562                 delete this.activeItem;
35563             }
35564         }
35565         this.fireEvent("mouseout", this, e, t);
35566     },
35567
35568     /**
35569      * Read-only.  Returns true if the menu is currently displayed, else false.
35570      * @type Boolean
35571      */
35572     isVisible : function(){
35573         return this.el && !this.hidden;
35574     },
35575
35576     /**
35577      * Displays this menu relative to another element
35578      * @param {String/HTMLElement/Roo.Element} element The element to align to
35579      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35580      * the element (defaults to this.defaultAlign)
35581      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35582      */
35583     show : function(el, pos, parentMenu){
35584         this.parentMenu = parentMenu;
35585         if(!this.el){
35586             this.render();
35587         }
35588         this.fireEvent("beforeshow", this);
35589         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35590     },
35591
35592     /**
35593      * Displays this menu at a specific xy position
35594      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35595      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35596      */
35597     showAt : function(xy, parentMenu, /* private: */_e){
35598         this.parentMenu = parentMenu;
35599         if(!this.el){
35600             this.render();
35601         }
35602         if(_e !== false){
35603             this.fireEvent("beforeshow", this);
35604             xy = this.el.adjustForConstraints(xy);
35605         }
35606         this.el.setXY(xy);
35607         this.el.show();
35608         this.hidden = false;
35609         this.focus();
35610         this.fireEvent("show", this);
35611     },
35612
35613     focus : function(){
35614         if(!this.hidden){
35615             this.doFocus.defer(50, this);
35616         }
35617     },
35618
35619     doFocus : function(){
35620         if(!this.hidden){
35621             this.focusEl.focus();
35622         }
35623     },
35624
35625     /**
35626      * Hides this menu and optionally all parent menus
35627      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35628      */
35629     hide : function(deep){
35630         if(this.el && this.isVisible()){
35631             this.fireEvent("beforehide", this);
35632             if(this.activeItem){
35633                 this.activeItem.deactivate();
35634                 this.activeItem = null;
35635             }
35636             this.el.hide();
35637             this.hidden = true;
35638             this.fireEvent("hide", this);
35639         }
35640         if(deep === true && this.parentMenu){
35641             this.parentMenu.hide(true);
35642         }
35643     },
35644
35645     /**
35646      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35647      * Any of the following are valid:
35648      * <ul>
35649      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35650      * <li>An HTMLElement object which will be converted to a menu item</li>
35651      * <li>A menu item config object that will be created as a new menu item</li>
35652      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35653      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35654      * </ul>
35655      * Usage:
35656      * <pre><code>
35657 // Create the menu
35658 var menu = new Roo.menu.Menu();
35659
35660 // Create a menu item to add by reference
35661 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35662
35663 // Add a bunch of items at once using different methods.
35664 // Only the last item added will be returned.
35665 var item = menu.add(
35666     menuItem,                // add existing item by ref
35667     'Dynamic Item',          // new TextItem
35668     '-',                     // new separator
35669     { text: 'Config Item' }  // new item by config
35670 );
35671 </code></pre>
35672      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35673      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35674      */
35675     add : function(){
35676         var a = arguments, l = a.length, item;
35677         for(var i = 0; i < l; i++){
35678             var el = a[i];
35679             if ((typeof(el) == "object") && el.xtype && el.xns) {
35680                 el = Roo.factory(el, Roo.menu);
35681             }
35682             
35683             if(el.render){ // some kind of Item
35684                 item = this.addItem(el);
35685             }else if(typeof el == "string"){ // string
35686                 if(el == "separator" || el == "-"){
35687                     item = this.addSeparator();
35688                 }else{
35689                     item = this.addText(el);
35690                 }
35691             }else if(el.tagName || el.el){ // element
35692                 item = this.addElement(el);
35693             }else if(typeof el == "object"){ // must be menu item config?
35694                 item = this.addMenuItem(el);
35695             }
35696         }
35697         return item;
35698     },
35699
35700     /**
35701      * Returns this menu's underlying {@link Roo.Element} object
35702      * @return {Roo.Element} The element
35703      */
35704     getEl : function(){
35705         if(!this.el){
35706             this.render();
35707         }
35708         return this.el;
35709     },
35710
35711     /**
35712      * Adds a separator bar to the menu
35713      * @return {Roo.menu.Item} The menu item that was added
35714      */
35715     addSeparator : function(){
35716         return this.addItem(new Roo.menu.Separator());
35717     },
35718
35719     /**
35720      * Adds an {@link Roo.Element} object to the menu
35721      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35722      * @return {Roo.menu.Item} The menu item that was added
35723      */
35724     addElement : function(el){
35725         return this.addItem(new Roo.menu.BaseItem(el));
35726     },
35727
35728     /**
35729      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35730      * @param {Roo.menu.Item} item The menu item to add
35731      * @return {Roo.menu.Item} The menu item that was added
35732      */
35733     addItem : function(item){
35734         this.items.add(item);
35735         if(this.ul){
35736             var li = document.createElement("li");
35737             li.className = "x-menu-list-item";
35738             this.ul.dom.appendChild(li);
35739             item.render(li, this);
35740             this.delayAutoWidth();
35741         }
35742         return item;
35743     },
35744
35745     /**
35746      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35747      * @param {Object} config A MenuItem config object
35748      * @return {Roo.menu.Item} The menu item that was added
35749      */
35750     addMenuItem : function(config){
35751         if(!(config instanceof Roo.menu.Item)){
35752             if(typeof config.checked == "boolean"){ // must be check menu item config?
35753                 config = new Roo.menu.CheckItem(config);
35754             }else{
35755                 config = new Roo.menu.Item(config);
35756             }
35757         }
35758         return this.addItem(config);
35759     },
35760
35761     /**
35762      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35763      * @param {String} text The text to display in the menu item
35764      * @return {Roo.menu.Item} The menu item that was added
35765      */
35766     addText : function(text){
35767         return this.addItem(new Roo.menu.TextItem({ text : text }));
35768     },
35769
35770     /**
35771      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35772      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35773      * @param {Roo.menu.Item} item The menu item to add
35774      * @return {Roo.menu.Item} The menu item that was added
35775      */
35776     insert : function(index, item){
35777         this.items.insert(index, item);
35778         if(this.ul){
35779             var li = document.createElement("li");
35780             li.className = "x-menu-list-item";
35781             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35782             item.render(li, this);
35783             this.delayAutoWidth();
35784         }
35785         return item;
35786     },
35787
35788     /**
35789      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35790      * @param {Roo.menu.Item} item The menu item to remove
35791      */
35792     remove : function(item){
35793         this.items.removeKey(item.id);
35794         item.destroy();
35795     },
35796
35797     /**
35798      * Removes and destroys all items in the menu
35799      */
35800     removeAll : function(){
35801         var f;
35802         while(f = this.items.first()){
35803             this.remove(f);
35804         }
35805     }
35806 });
35807
35808 // MenuNav is a private utility class used internally by the Menu
35809 Roo.menu.MenuNav = function(menu){
35810     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35811     this.scope = this.menu = menu;
35812 };
35813
35814 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35815     doRelay : function(e, h){
35816         var k = e.getKey();
35817         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35818             this.menu.tryActivate(0, 1);
35819             return false;
35820         }
35821         return h.call(this.scope || this, e, this.menu);
35822     },
35823
35824     up : function(e, m){
35825         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35826             m.tryActivate(m.items.length-1, -1);
35827         }
35828     },
35829
35830     down : function(e, m){
35831         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35832             m.tryActivate(0, 1);
35833         }
35834     },
35835
35836     right : function(e, m){
35837         if(m.activeItem){
35838             m.activeItem.expandMenu(true);
35839         }
35840     },
35841
35842     left : function(e, m){
35843         m.hide();
35844         if(m.parentMenu && m.parentMenu.activeItem){
35845             m.parentMenu.activeItem.activate();
35846         }
35847     },
35848
35849     enter : function(e, m){
35850         if(m.activeItem){
35851             e.stopPropagation();
35852             m.activeItem.onClick(e);
35853             m.fireEvent("click", this, m.activeItem);
35854             return true;
35855         }
35856     }
35857 });/*
35858  * Based on:
35859  * Ext JS Library 1.1.1
35860  * Copyright(c) 2006-2007, Ext JS, LLC.
35861  *
35862  * Originally Released Under LGPL - original licence link has changed is not relivant.
35863  *
35864  * Fork - LGPL
35865  * <script type="text/javascript">
35866  */
35867  
35868 /**
35869  * @class Roo.menu.MenuMgr
35870  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35871  * @singleton
35872  */
35873 Roo.menu.MenuMgr = function(){
35874    var menus, active, groups = {}, attached = false, lastShow = new Date();
35875
35876    // private - called when first menu is created
35877    function init(){
35878        menus = {};
35879        active = new Roo.util.MixedCollection();
35880        Roo.get(document).addKeyListener(27, function(){
35881            if(active.length > 0){
35882                hideAll();
35883            }
35884        });
35885    }
35886
35887    // private
35888    function hideAll(){
35889        if(active && active.length > 0){
35890            var c = active.clone();
35891            c.each(function(m){
35892                m.hide();
35893            });
35894        }
35895    }
35896
35897    // private
35898    function onHide(m){
35899        active.remove(m);
35900        if(active.length < 1){
35901            Roo.get(document).un("mousedown", onMouseDown);
35902            attached = false;
35903        }
35904    }
35905
35906    // private
35907    function onShow(m){
35908        var last = active.last();
35909        lastShow = new Date();
35910        active.add(m);
35911        if(!attached){
35912            Roo.get(document).on("mousedown", onMouseDown);
35913            attached = true;
35914        }
35915        if(m.parentMenu){
35916           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35917           m.parentMenu.activeChild = m;
35918        }else if(last && last.isVisible()){
35919           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35920        }
35921    }
35922
35923    // private
35924    function onBeforeHide(m){
35925        if(m.activeChild){
35926            m.activeChild.hide();
35927        }
35928        if(m.autoHideTimer){
35929            clearTimeout(m.autoHideTimer);
35930            delete m.autoHideTimer;
35931        }
35932    }
35933
35934    // private
35935    function onBeforeShow(m){
35936        var pm = m.parentMenu;
35937        if(!pm && !m.allowOtherMenus){
35938            hideAll();
35939        }else if(pm && pm.activeChild && active != m){
35940            pm.activeChild.hide();
35941        }
35942    }
35943
35944    // private
35945    function onMouseDown(e){
35946        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35947            hideAll();
35948        }
35949    }
35950
35951    // private
35952    function onBeforeCheck(mi, state){
35953        if(state){
35954            var g = groups[mi.group];
35955            for(var i = 0, l = g.length; i < l; i++){
35956                if(g[i] != mi){
35957                    g[i].setChecked(false);
35958                }
35959            }
35960        }
35961    }
35962
35963    return {
35964
35965        /**
35966         * Hides all menus that are currently visible
35967         */
35968        hideAll : function(){
35969             hideAll();  
35970        },
35971
35972        // private
35973        register : function(menu){
35974            if(!menus){
35975                init();
35976            }
35977            menus[menu.id] = menu;
35978            menu.on("beforehide", onBeforeHide);
35979            menu.on("hide", onHide);
35980            menu.on("beforeshow", onBeforeShow);
35981            menu.on("show", onShow);
35982            var g = menu.group;
35983            if(g && menu.events["checkchange"]){
35984                if(!groups[g]){
35985                    groups[g] = [];
35986                }
35987                groups[g].push(menu);
35988                menu.on("checkchange", onCheck);
35989            }
35990        },
35991
35992         /**
35993          * Returns a {@link Roo.menu.Menu} object
35994          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35995          * be used to generate and return a new Menu instance.
35996          */
35997        get : function(menu){
35998            if(typeof menu == "string"){ // menu id
35999                return menus[menu];
36000            }else if(menu.events){  // menu instance
36001                return menu;
36002            }else if(typeof menu.length == 'number'){ // array of menu items?
36003                return new Roo.menu.Menu({items:menu});
36004            }else{ // otherwise, must be a config
36005                return new Roo.menu.Menu(menu);
36006            }
36007        },
36008
36009        // private
36010        unregister : function(menu){
36011            delete menus[menu.id];
36012            menu.un("beforehide", onBeforeHide);
36013            menu.un("hide", onHide);
36014            menu.un("beforeshow", onBeforeShow);
36015            menu.un("show", onShow);
36016            var g = menu.group;
36017            if(g && menu.events["checkchange"]){
36018                groups[g].remove(menu);
36019                menu.un("checkchange", onCheck);
36020            }
36021        },
36022
36023        // private
36024        registerCheckable : function(menuItem){
36025            var g = menuItem.group;
36026            if(g){
36027                if(!groups[g]){
36028                    groups[g] = [];
36029                }
36030                groups[g].push(menuItem);
36031                menuItem.on("beforecheckchange", onBeforeCheck);
36032            }
36033        },
36034
36035        // private
36036        unregisterCheckable : function(menuItem){
36037            var g = menuItem.group;
36038            if(g){
36039                groups[g].remove(menuItem);
36040                menuItem.un("beforecheckchange", onBeforeCheck);
36041            }
36042        }
36043    };
36044 }();/*
36045  * Based on:
36046  * Ext JS Library 1.1.1
36047  * Copyright(c) 2006-2007, Ext JS, LLC.
36048  *
36049  * Originally Released Under LGPL - original licence link has changed is not relivant.
36050  *
36051  * Fork - LGPL
36052  * <script type="text/javascript">
36053  */
36054  
36055
36056 /**
36057  * @class Roo.menu.BaseItem
36058  * @extends Roo.Component
36059  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36060  * management and base configuration options shared by all menu components.
36061  * @constructor
36062  * Creates a new BaseItem
36063  * @param {Object} config Configuration options
36064  */
36065 Roo.menu.BaseItem = function(config){
36066     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36067
36068     this.addEvents({
36069         /**
36070          * @event click
36071          * Fires when this item is clicked
36072          * @param {Roo.menu.BaseItem} this
36073          * @param {Roo.EventObject} e
36074          */
36075         click: true,
36076         /**
36077          * @event activate
36078          * Fires when this item is activated
36079          * @param {Roo.menu.BaseItem} this
36080          */
36081         activate : true,
36082         /**
36083          * @event deactivate
36084          * Fires when this item is deactivated
36085          * @param {Roo.menu.BaseItem} this
36086          */
36087         deactivate : true
36088     });
36089
36090     if(this.handler){
36091         this.on("click", this.handler, this.scope, true);
36092     }
36093 };
36094
36095 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36096     /**
36097      * @cfg {Function} handler
36098      * A function that will handle the click event of this menu item (defaults to undefined)
36099      */
36100     /**
36101      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36102      */
36103     canActivate : false,
36104     
36105      /**
36106      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36107      */
36108     hidden: false,
36109     
36110     /**
36111      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36112      */
36113     activeClass : "x-menu-item-active",
36114     /**
36115      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36116      */
36117     hideOnClick : true,
36118     /**
36119      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36120      */
36121     hideDelay : 100,
36122
36123     // private
36124     ctype: "Roo.menu.BaseItem",
36125
36126     // private
36127     actionMode : "container",
36128
36129     // private
36130     render : function(container, parentMenu){
36131         this.parentMenu = parentMenu;
36132         Roo.menu.BaseItem.superclass.render.call(this, container);
36133         this.container.menuItemId = this.id;
36134     },
36135
36136     // private
36137     onRender : function(container, position){
36138         this.el = Roo.get(this.el);
36139         container.dom.appendChild(this.el.dom);
36140     },
36141
36142     // private
36143     onClick : function(e){
36144         if(!this.disabled && this.fireEvent("click", this, e) !== false
36145                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36146             this.handleClick(e);
36147         }else{
36148             e.stopEvent();
36149         }
36150     },
36151
36152     // private
36153     activate : function(){
36154         if(this.disabled){
36155             return false;
36156         }
36157         var li = this.container;
36158         li.addClass(this.activeClass);
36159         this.region = li.getRegion().adjust(2, 2, -2, -2);
36160         this.fireEvent("activate", this);
36161         return true;
36162     },
36163
36164     // private
36165     deactivate : function(){
36166         this.container.removeClass(this.activeClass);
36167         this.fireEvent("deactivate", this);
36168     },
36169
36170     // private
36171     shouldDeactivate : function(e){
36172         return !this.region || !this.region.contains(e.getPoint());
36173     },
36174
36175     // private
36176     handleClick : function(e){
36177         if(this.hideOnClick){
36178             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36179         }
36180     },
36181
36182     // private
36183     expandMenu : function(autoActivate){
36184         // do nothing
36185     },
36186
36187     // private
36188     hideMenu : function(){
36189         // do nothing
36190     }
36191 });/*
36192  * Based on:
36193  * Ext JS Library 1.1.1
36194  * Copyright(c) 2006-2007, Ext JS, LLC.
36195  *
36196  * Originally Released Under LGPL - original licence link has changed is not relivant.
36197  *
36198  * Fork - LGPL
36199  * <script type="text/javascript">
36200  */
36201  
36202 /**
36203  * @class Roo.menu.Adapter
36204  * @extends Roo.menu.BaseItem
36205  * 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.
36206  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36207  * @constructor
36208  * Creates a new Adapter
36209  * @param {Object} config Configuration options
36210  */
36211 Roo.menu.Adapter = function(component, config){
36212     Roo.menu.Adapter.superclass.constructor.call(this, config);
36213     this.component = component;
36214 };
36215 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36216     // private
36217     canActivate : true,
36218
36219     // private
36220     onRender : function(container, position){
36221         this.component.render(container);
36222         this.el = this.component.getEl();
36223     },
36224
36225     // private
36226     activate : function(){
36227         if(this.disabled){
36228             return false;
36229         }
36230         this.component.focus();
36231         this.fireEvent("activate", this);
36232         return true;
36233     },
36234
36235     // private
36236     deactivate : function(){
36237         this.fireEvent("deactivate", this);
36238     },
36239
36240     // private
36241     disable : function(){
36242         this.component.disable();
36243         Roo.menu.Adapter.superclass.disable.call(this);
36244     },
36245
36246     // private
36247     enable : function(){
36248         this.component.enable();
36249         Roo.menu.Adapter.superclass.enable.call(this);
36250     }
36251 });/*
36252  * Based on:
36253  * Ext JS Library 1.1.1
36254  * Copyright(c) 2006-2007, Ext JS, LLC.
36255  *
36256  * Originally Released Under LGPL - original licence link has changed is not relivant.
36257  *
36258  * Fork - LGPL
36259  * <script type="text/javascript">
36260  */
36261
36262 /**
36263  * @class Roo.menu.TextItem
36264  * @extends Roo.menu.BaseItem
36265  * Adds a static text string to a menu, usually used as either a heading or group separator.
36266  * Note: old style constructor with text is still supported.
36267  * 
36268  * @constructor
36269  * Creates a new TextItem
36270  * @param {Object} cfg Configuration
36271  */
36272 Roo.menu.TextItem = function(cfg){
36273     if (typeof(cfg) == 'string') {
36274         this.text = cfg;
36275     } else {
36276         Roo.apply(this,cfg);
36277     }
36278     
36279     Roo.menu.TextItem.superclass.constructor.call(this);
36280 };
36281
36282 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36283     /**
36284      * @cfg {Boolean} text Text to show on item.
36285      */
36286     text : '',
36287     
36288     /**
36289      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36290      */
36291     hideOnClick : false,
36292     /**
36293      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36294      */
36295     itemCls : "x-menu-text",
36296
36297     // private
36298     onRender : function(){
36299         var s = document.createElement("span");
36300         s.className = this.itemCls;
36301         s.innerHTML = this.text;
36302         this.el = s;
36303         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36304     }
36305 });/*
36306  * Based on:
36307  * Ext JS Library 1.1.1
36308  * Copyright(c) 2006-2007, Ext JS, LLC.
36309  *
36310  * Originally Released Under LGPL - original licence link has changed is not relivant.
36311  *
36312  * Fork - LGPL
36313  * <script type="text/javascript">
36314  */
36315
36316 /**
36317  * @class Roo.menu.Separator
36318  * @extends Roo.menu.BaseItem
36319  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36320  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36321  * @constructor
36322  * @param {Object} config Configuration options
36323  */
36324 Roo.menu.Separator = function(config){
36325     Roo.menu.Separator.superclass.constructor.call(this, config);
36326 };
36327
36328 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36329     /**
36330      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36331      */
36332     itemCls : "x-menu-sep",
36333     /**
36334      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36335      */
36336     hideOnClick : false,
36337
36338     // private
36339     onRender : function(li){
36340         var s = document.createElement("span");
36341         s.className = this.itemCls;
36342         s.innerHTML = "&#160;";
36343         this.el = s;
36344         li.addClass("x-menu-sep-li");
36345         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36346     }
36347 });/*
36348  * Based on:
36349  * Ext JS Library 1.1.1
36350  * Copyright(c) 2006-2007, Ext JS, LLC.
36351  *
36352  * Originally Released Under LGPL - original licence link has changed is not relivant.
36353  *
36354  * Fork - LGPL
36355  * <script type="text/javascript">
36356  */
36357 /**
36358  * @class Roo.menu.Item
36359  * @extends Roo.menu.BaseItem
36360  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36361  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36362  * activation and click handling.
36363  * @constructor
36364  * Creates a new Item
36365  * @param {Object} config Configuration options
36366  */
36367 Roo.menu.Item = function(config){
36368     Roo.menu.Item.superclass.constructor.call(this, config);
36369     if(this.menu){
36370         this.menu = Roo.menu.MenuMgr.get(this.menu);
36371     }
36372 };
36373 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36374     
36375     /**
36376      * @cfg {String} text
36377      * The text to show on the menu item.
36378      */
36379     text: '',
36380      /**
36381      * @cfg {String} HTML to render in menu
36382      * The text to show on the menu item (HTML version).
36383      */
36384     html: '',
36385     /**
36386      * @cfg {String} icon
36387      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36388      */
36389     icon: undefined,
36390     /**
36391      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36392      */
36393     itemCls : "x-menu-item",
36394     /**
36395      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36396      */
36397     canActivate : true,
36398     /**
36399      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36400      */
36401     showDelay: 200,
36402     // doc'd in BaseItem
36403     hideDelay: 200,
36404
36405     // private
36406     ctype: "Roo.menu.Item",
36407     
36408     // private
36409     onRender : function(container, position){
36410         var el = document.createElement("a");
36411         el.hideFocus = true;
36412         el.unselectable = "on";
36413         el.href = this.href || "#";
36414         if(this.hrefTarget){
36415             el.target = this.hrefTarget;
36416         }
36417         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36418         
36419         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36420         
36421         el.innerHTML = String.format(
36422                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36423                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36424         this.el = el;
36425         Roo.menu.Item.superclass.onRender.call(this, container, position);
36426     },
36427
36428     /**
36429      * Sets the text to display in this menu item
36430      * @param {String} text The text to display
36431      * @param {Boolean} isHTML true to indicate text is pure html.
36432      */
36433     setText : function(text, isHTML){
36434         if (isHTML) {
36435             this.html = text;
36436         } else {
36437             this.text = text;
36438             this.html = '';
36439         }
36440         if(this.rendered){
36441             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36442      
36443             this.el.update(String.format(
36444                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36445                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36446             this.parentMenu.autoWidth();
36447         }
36448     },
36449
36450     // private
36451     handleClick : function(e){
36452         if(!this.href){ // if no link defined, stop the event automatically
36453             e.stopEvent();
36454         }
36455         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36456     },
36457
36458     // private
36459     activate : function(autoExpand){
36460         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36461             this.focus();
36462             if(autoExpand){
36463                 this.expandMenu();
36464             }
36465         }
36466         return true;
36467     },
36468
36469     // private
36470     shouldDeactivate : function(e){
36471         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36472             if(this.menu && this.menu.isVisible()){
36473                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36474             }
36475             return true;
36476         }
36477         return false;
36478     },
36479
36480     // private
36481     deactivate : function(){
36482         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36483         this.hideMenu();
36484     },
36485
36486     // private
36487     expandMenu : function(autoActivate){
36488         if(!this.disabled && this.menu){
36489             clearTimeout(this.hideTimer);
36490             delete this.hideTimer;
36491             if(!this.menu.isVisible() && !this.showTimer){
36492                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36493             }else if (this.menu.isVisible() && autoActivate){
36494                 this.menu.tryActivate(0, 1);
36495             }
36496         }
36497     },
36498
36499     // private
36500     deferExpand : function(autoActivate){
36501         delete this.showTimer;
36502         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36503         if(autoActivate){
36504             this.menu.tryActivate(0, 1);
36505         }
36506     },
36507
36508     // private
36509     hideMenu : function(){
36510         clearTimeout(this.showTimer);
36511         delete this.showTimer;
36512         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36513             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36514         }
36515     },
36516
36517     // private
36518     deferHide : function(){
36519         delete this.hideTimer;
36520         this.menu.hide();
36521     }
36522 });/*
36523  * Based on:
36524  * Ext JS Library 1.1.1
36525  * Copyright(c) 2006-2007, Ext JS, LLC.
36526  *
36527  * Originally Released Under LGPL - original licence link has changed is not relivant.
36528  *
36529  * Fork - LGPL
36530  * <script type="text/javascript">
36531  */
36532  
36533 /**
36534  * @class Roo.menu.CheckItem
36535  * @extends Roo.menu.Item
36536  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36537  * @constructor
36538  * Creates a new CheckItem
36539  * @param {Object} config Configuration options
36540  */
36541 Roo.menu.CheckItem = function(config){
36542     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36543     this.addEvents({
36544         /**
36545          * @event beforecheckchange
36546          * Fires before the checked value is set, providing an opportunity to cancel if needed
36547          * @param {Roo.menu.CheckItem} this
36548          * @param {Boolean} checked The new checked value that will be set
36549          */
36550         "beforecheckchange" : true,
36551         /**
36552          * @event checkchange
36553          * Fires after the checked value has been set
36554          * @param {Roo.menu.CheckItem} this
36555          * @param {Boolean} checked The checked value that was set
36556          */
36557         "checkchange" : true
36558     });
36559     if(this.checkHandler){
36560         this.on('checkchange', this.checkHandler, this.scope);
36561     }
36562 };
36563 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36564     /**
36565      * @cfg {String} group
36566      * All check items with the same group name will automatically be grouped into a single-select
36567      * radio button group (defaults to '')
36568      */
36569     /**
36570      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36571      */
36572     itemCls : "x-menu-item x-menu-check-item",
36573     /**
36574      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36575      */
36576     groupClass : "x-menu-group-item",
36577
36578     /**
36579      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36580      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36581      * initialized with checked = true will be rendered as checked.
36582      */
36583     checked: false,
36584
36585     // private
36586     ctype: "Roo.menu.CheckItem",
36587
36588     // private
36589     onRender : function(c){
36590         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36591         if(this.group){
36592             this.el.addClass(this.groupClass);
36593         }
36594         Roo.menu.MenuMgr.registerCheckable(this);
36595         if(this.checked){
36596             this.checked = false;
36597             this.setChecked(true, true);
36598         }
36599     },
36600
36601     // private
36602     destroy : function(){
36603         if(this.rendered){
36604             Roo.menu.MenuMgr.unregisterCheckable(this);
36605         }
36606         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36607     },
36608
36609     /**
36610      * Set the checked state of this item
36611      * @param {Boolean} checked The new checked value
36612      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36613      */
36614     setChecked : function(state, suppressEvent){
36615         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36616             if(this.container){
36617                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36618             }
36619             this.checked = state;
36620             if(suppressEvent !== true){
36621                 this.fireEvent("checkchange", this, state);
36622             }
36623         }
36624     },
36625
36626     // private
36627     handleClick : function(e){
36628        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36629            this.setChecked(!this.checked);
36630        }
36631        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36632     }
36633 });/*
36634  * Based on:
36635  * Ext JS Library 1.1.1
36636  * Copyright(c) 2006-2007, Ext JS, LLC.
36637  *
36638  * Originally Released Under LGPL - original licence link has changed is not relivant.
36639  *
36640  * Fork - LGPL
36641  * <script type="text/javascript">
36642  */
36643  
36644 /**
36645  * @class Roo.menu.DateItem
36646  * @extends Roo.menu.Adapter
36647  * A menu item that wraps the {@link Roo.DatPicker} component.
36648  * @constructor
36649  * Creates a new DateItem
36650  * @param {Object} config Configuration options
36651  */
36652 Roo.menu.DateItem = function(config){
36653     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36654     /** The Roo.DatePicker object @type Roo.DatePicker */
36655     this.picker = this.component;
36656     this.addEvents({select: true});
36657     
36658     this.picker.on("render", function(picker){
36659         picker.getEl().swallowEvent("click");
36660         picker.container.addClass("x-menu-date-item");
36661     });
36662
36663     this.picker.on("select", this.onSelect, this);
36664 };
36665
36666 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36667     // private
36668     onSelect : function(picker, date){
36669         this.fireEvent("select", this, date, picker);
36670         Roo.menu.DateItem.superclass.handleClick.call(this);
36671     }
36672 });/*
36673  * Based on:
36674  * Ext JS Library 1.1.1
36675  * Copyright(c) 2006-2007, Ext JS, LLC.
36676  *
36677  * Originally Released Under LGPL - original licence link has changed is not relivant.
36678  *
36679  * Fork - LGPL
36680  * <script type="text/javascript">
36681  */
36682  
36683 /**
36684  * @class Roo.menu.ColorItem
36685  * @extends Roo.menu.Adapter
36686  * A menu item that wraps the {@link Roo.ColorPalette} component.
36687  * @constructor
36688  * Creates a new ColorItem
36689  * @param {Object} config Configuration options
36690  */
36691 Roo.menu.ColorItem = function(config){
36692     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36693     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36694     this.palette = this.component;
36695     this.relayEvents(this.palette, ["select"]);
36696     if(this.selectHandler){
36697         this.on('select', this.selectHandler, this.scope);
36698     }
36699 };
36700 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36701  * Based on:
36702  * Ext JS Library 1.1.1
36703  * Copyright(c) 2006-2007, Ext JS, LLC.
36704  *
36705  * Originally Released Under LGPL - original licence link has changed is not relivant.
36706  *
36707  * Fork - LGPL
36708  * <script type="text/javascript">
36709  */
36710  
36711
36712 /**
36713  * @class Roo.menu.DateMenu
36714  * @extends Roo.menu.Menu
36715  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36716  * @constructor
36717  * Creates a new DateMenu
36718  * @param {Object} config Configuration options
36719  */
36720 Roo.menu.DateMenu = function(config){
36721     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36722     this.plain = true;
36723     var di = new Roo.menu.DateItem(config);
36724     this.add(di);
36725     /**
36726      * The {@link Roo.DatePicker} instance for this DateMenu
36727      * @type DatePicker
36728      */
36729     this.picker = di.picker;
36730     /**
36731      * @event select
36732      * @param {DatePicker} picker
36733      * @param {Date} date
36734      */
36735     this.relayEvents(di, ["select"]);
36736     this.on('beforeshow', function(){
36737         if(this.picker){
36738             this.picker.hideMonthPicker(false);
36739         }
36740     }, this);
36741 };
36742 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36743     cls:'x-date-menu'
36744 });/*
36745  * Based on:
36746  * Ext JS Library 1.1.1
36747  * Copyright(c) 2006-2007, Ext JS, LLC.
36748  *
36749  * Originally Released Under LGPL - original licence link has changed is not relivant.
36750  *
36751  * Fork - LGPL
36752  * <script type="text/javascript">
36753  */
36754  
36755
36756 /**
36757  * @class Roo.menu.ColorMenu
36758  * @extends Roo.menu.Menu
36759  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36760  * @constructor
36761  * Creates a new ColorMenu
36762  * @param {Object} config Configuration options
36763  */
36764 Roo.menu.ColorMenu = function(config){
36765     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36766     this.plain = true;
36767     var ci = new Roo.menu.ColorItem(config);
36768     this.add(ci);
36769     /**
36770      * The {@link Roo.ColorPalette} instance for this ColorMenu
36771      * @type ColorPalette
36772      */
36773     this.palette = ci.palette;
36774     /**
36775      * @event select
36776      * @param {ColorPalette} palette
36777      * @param {String} color
36778      */
36779     this.relayEvents(ci, ["select"]);
36780 };
36781 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36782  * Based on:
36783  * Ext JS Library 1.1.1
36784  * Copyright(c) 2006-2007, Ext JS, LLC.
36785  *
36786  * Originally Released Under LGPL - original licence link has changed is not relivant.
36787  *
36788  * Fork - LGPL
36789  * <script type="text/javascript">
36790  */
36791  
36792 /**
36793  * @class Roo.form.Field
36794  * @extends Roo.BoxComponent
36795  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36796  * @constructor
36797  * Creates a new Field
36798  * @param {Object} config Configuration options
36799  */
36800 Roo.form.Field = function(config){
36801     Roo.form.Field.superclass.constructor.call(this, config);
36802 };
36803
36804 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36805     /**
36806      * @cfg {String} fieldLabel Label to use when rendering a form.
36807      */
36808        /**
36809      * @cfg {String} qtip Mouse over tip
36810      */
36811      
36812     /**
36813      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36814      */
36815     invalidClass : "x-form-invalid",
36816     /**
36817      * @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")
36818      */
36819     invalidText : "The value in this field is invalid",
36820     /**
36821      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36822      */
36823     focusClass : "x-form-focus",
36824     /**
36825      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36826       automatic validation (defaults to "keyup").
36827      */
36828     validationEvent : "keyup",
36829     /**
36830      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36831      */
36832     validateOnBlur : true,
36833     /**
36834      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36835      */
36836     validationDelay : 250,
36837     /**
36838      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36839      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36840      */
36841     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36842     /**
36843      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36844      */
36845     fieldClass : "x-form-field",
36846     /**
36847      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36848      *<pre>
36849 Value         Description
36850 -----------   ----------------------------------------------------------------------
36851 qtip          Display a quick tip when the user hovers over the field
36852 title         Display a default browser title attribute popup
36853 under         Add a block div beneath the field containing the error text
36854 side          Add an error icon to the right of the field with a popup on hover
36855 [element id]  Add the error text directly to the innerHTML of the specified element
36856 </pre>
36857      */
36858     msgTarget : 'qtip',
36859     /**
36860      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36861      */
36862     msgFx : 'normal',
36863
36864     /**
36865      * @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.
36866      */
36867     readOnly : false,
36868
36869     /**
36870      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36871      */
36872     disabled : false,
36873
36874     /**
36875      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36876      */
36877     inputType : undefined,
36878     
36879     /**
36880      * @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).
36881          */
36882         tabIndex : undefined,
36883         
36884     // private
36885     isFormField : true,
36886
36887     // private
36888     hasFocus : false,
36889     /**
36890      * @property {Roo.Element} fieldEl
36891      * Element Containing the rendered Field (with label etc.)
36892      */
36893     /**
36894      * @cfg {Mixed} value A value to initialize this field with.
36895      */
36896     value : undefined,
36897
36898     /**
36899      * @cfg {String} name The field's HTML name attribute.
36900      */
36901     /**
36902      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36903      */
36904
36905         // private ??
36906         initComponent : function(){
36907         Roo.form.Field.superclass.initComponent.call(this);
36908         this.addEvents({
36909             /**
36910              * @event focus
36911              * Fires when this field receives input focus.
36912              * @param {Roo.form.Field} this
36913              */
36914             focus : true,
36915             /**
36916              * @event blur
36917              * Fires when this field loses input focus.
36918              * @param {Roo.form.Field} this
36919              */
36920             blur : true,
36921             /**
36922              * @event specialkey
36923              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36924              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36925              * @param {Roo.form.Field} this
36926              * @param {Roo.EventObject} e The event object
36927              */
36928             specialkey : true,
36929             /**
36930              * @event change
36931              * Fires just before the field blurs if the field value has changed.
36932              * @param {Roo.form.Field} this
36933              * @param {Mixed} newValue The new value
36934              * @param {Mixed} oldValue The original value
36935              */
36936             change : true,
36937             /**
36938              * @event invalid
36939              * Fires after the field has been marked as invalid.
36940              * @param {Roo.form.Field} this
36941              * @param {String} msg The validation message
36942              */
36943             invalid : true,
36944             /**
36945              * @event valid
36946              * Fires after the field has been validated with no errors.
36947              * @param {Roo.form.Field} this
36948              */
36949             valid : true,
36950              /**
36951              * @event keyup
36952              * Fires after the key up
36953              * @param {Roo.form.Field} this
36954              * @param {Roo.EventObject}  e The event Object
36955              */
36956             keyup : true
36957         });
36958     },
36959
36960     /**
36961      * Returns the name attribute of the field if available
36962      * @return {String} name The field name
36963      */
36964     getName: function(){
36965          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36966     },
36967
36968     // private
36969     onRender : function(ct, position){
36970         Roo.form.Field.superclass.onRender.call(this, ct, position);
36971         if(!this.el){
36972             var cfg = this.getAutoCreate();
36973             if(!cfg.name){
36974                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36975             }
36976             if (!cfg.name.length) {
36977                 delete cfg.name;
36978             }
36979             if(this.inputType){
36980                 cfg.type = this.inputType;
36981             }
36982             this.el = ct.createChild(cfg, position);
36983         }
36984         var type = this.el.dom.type;
36985         if(type){
36986             if(type == 'password'){
36987                 type = 'text';
36988             }
36989             this.el.addClass('x-form-'+type);
36990         }
36991         if(this.readOnly){
36992             this.el.dom.readOnly = true;
36993         }
36994         if(this.tabIndex !== undefined){
36995             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36996         }
36997
36998         this.el.addClass([this.fieldClass, this.cls]);
36999         this.initValue();
37000     },
37001
37002     /**
37003      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37004      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37005      * @return {Roo.form.Field} this
37006      */
37007     applyTo : function(target){
37008         this.allowDomMove = false;
37009         this.el = Roo.get(target);
37010         this.render(this.el.dom.parentNode);
37011         return this;
37012     },
37013
37014     // private
37015     initValue : function(){
37016         if(this.value !== undefined){
37017             this.setValue(this.value);
37018         }else if(this.el.dom.value.length > 0){
37019             this.setValue(this.el.dom.value);
37020         }
37021     },
37022
37023     /**
37024      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37025      */
37026     isDirty : function() {
37027         if(this.disabled) {
37028             return false;
37029         }
37030         return String(this.getValue()) !== String(this.originalValue);
37031     },
37032
37033     // private
37034     afterRender : function(){
37035         Roo.form.Field.superclass.afterRender.call(this);
37036         this.initEvents();
37037     },
37038
37039     // private
37040     fireKey : function(e){
37041         //Roo.log('field ' + e.getKey());
37042         if(e.isNavKeyPress()){
37043             this.fireEvent("specialkey", this, e);
37044         }
37045     },
37046
37047     /**
37048      * Resets the current field value to the originally loaded value and clears any validation messages
37049      */
37050     reset : function(){
37051         this.setValue(this.resetValue);
37052         this.clearInvalid();
37053     },
37054
37055     // private
37056     initEvents : function(){
37057         // safari killled keypress - so keydown is now used..
37058         this.el.on("keydown" , this.fireKey,  this);
37059         this.el.on("focus", this.onFocus,  this);
37060         this.el.on("blur", this.onBlur,  this);
37061         this.el.relayEvent('keyup', this);
37062
37063         // reference to original value for reset
37064         this.originalValue = this.getValue();
37065         this.resetValue =  this.getValue();
37066     },
37067
37068     // private
37069     onFocus : function(){
37070         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37071             this.el.addClass(this.focusClass);
37072         }
37073         if(!this.hasFocus){
37074             this.hasFocus = true;
37075             this.startValue = this.getValue();
37076             this.fireEvent("focus", this);
37077         }
37078     },
37079
37080     beforeBlur : Roo.emptyFn,
37081
37082     // private
37083     onBlur : function(){
37084         this.beforeBlur();
37085         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37086             this.el.removeClass(this.focusClass);
37087         }
37088         this.hasFocus = false;
37089         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37090             this.validate();
37091         }
37092         var v = this.getValue();
37093         if(String(v) !== String(this.startValue)){
37094             this.fireEvent('change', this, v, this.startValue);
37095         }
37096         this.fireEvent("blur", this);
37097     },
37098
37099     /**
37100      * Returns whether or not the field value is currently valid
37101      * @param {Boolean} preventMark True to disable marking the field invalid
37102      * @return {Boolean} True if the value is valid, else false
37103      */
37104     isValid : function(preventMark){
37105         if(this.disabled){
37106             return true;
37107         }
37108         var restore = this.preventMark;
37109         this.preventMark = preventMark === true;
37110         var v = this.validateValue(this.processValue(this.getRawValue()));
37111         this.preventMark = restore;
37112         return v;
37113     },
37114
37115     /**
37116      * Validates the field value
37117      * @return {Boolean} True if the value is valid, else false
37118      */
37119     validate : function(){
37120         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37121             this.clearInvalid();
37122             return true;
37123         }
37124         return false;
37125     },
37126
37127     processValue : function(value){
37128         return value;
37129     },
37130
37131     // private
37132     // Subclasses should provide the validation implementation by overriding this
37133     validateValue : function(value){
37134         return true;
37135     },
37136
37137     /**
37138      * Mark this field as invalid
37139      * @param {String} msg The validation message
37140      */
37141     markInvalid : function(msg){
37142         if(!this.rendered || this.preventMark){ // not rendered
37143             return;
37144         }
37145         
37146         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37147         
37148         obj.el.addClass(this.invalidClass);
37149         msg = msg || this.invalidText;
37150         switch(this.msgTarget){
37151             case 'qtip':
37152                 obj.el.dom.qtip = msg;
37153                 obj.el.dom.qclass = 'x-form-invalid-tip';
37154                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37155                     Roo.QuickTips.enable();
37156                 }
37157                 break;
37158             case 'title':
37159                 this.el.dom.title = msg;
37160                 break;
37161             case 'under':
37162                 if(!this.errorEl){
37163                     var elp = this.el.findParent('.x-form-element', 5, true);
37164                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37165                     this.errorEl.setWidth(elp.getWidth(true)-20);
37166                 }
37167                 this.errorEl.update(msg);
37168                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37169                 break;
37170             case 'side':
37171                 if(!this.errorIcon){
37172                     var elp = this.el.findParent('.x-form-element', 5, true);
37173                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37174                 }
37175                 this.alignErrorIcon();
37176                 this.errorIcon.dom.qtip = msg;
37177                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37178                 this.errorIcon.show();
37179                 this.on('resize', this.alignErrorIcon, this);
37180                 break;
37181             default:
37182                 var t = Roo.getDom(this.msgTarget);
37183                 t.innerHTML = msg;
37184                 t.style.display = this.msgDisplay;
37185                 break;
37186         }
37187         this.fireEvent('invalid', this, msg);
37188     },
37189
37190     // private
37191     alignErrorIcon : function(){
37192         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37193     },
37194
37195     /**
37196      * Clear any invalid styles/messages for this field
37197      */
37198     clearInvalid : function(){
37199         if(!this.rendered || this.preventMark){ // not rendered
37200             return;
37201         }
37202         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37203         
37204         obj.el.removeClass(this.invalidClass);
37205         switch(this.msgTarget){
37206             case 'qtip':
37207                 obj.el.dom.qtip = '';
37208                 break;
37209             case 'title':
37210                 this.el.dom.title = '';
37211                 break;
37212             case 'under':
37213                 if(this.errorEl){
37214                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37215                 }
37216                 break;
37217             case 'side':
37218                 if(this.errorIcon){
37219                     this.errorIcon.dom.qtip = '';
37220                     this.errorIcon.hide();
37221                     this.un('resize', this.alignErrorIcon, this);
37222                 }
37223                 break;
37224             default:
37225                 var t = Roo.getDom(this.msgTarget);
37226                 t.innerHTML = '';
37227                 t.style.display = 'none';
37228                 break;
37229         }
37230         this.fireEvent('valid', this);
37231     },
37232
37233     /**
37234      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37235      * @return {Mixed} value The field value
37236      */
37237     getRawValue : function(){
37238         var v = this.el.getValue();
37239         
37240         return v;
37241     },
37242
37243     /**
37244      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37245      * @return {Mixed} value The field value
37246      */
37247     getValue : function(){
37248         var v = this.el.getValue();
37249          
37250         return v;
37251     },
37252
37253     /**
37254      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37255      * @param {Mixed} value The value to set
37256      */
37257     setRawValue : function(v){
37258         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37259     },
37260
37261     /**
37262      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37263      * @param {Mixed} value The value to set
37264      */
37265     setValue : function(v){
37266         this.value = v;
37267         if(this.rendered){
37268             this.el.dom.value = (v === null || v === undefined ? '' : v);
37269              this.validate();
37270         }
37271     },
37272
37273     adjustSize : function(w, h){
37274         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37275         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37276         return s;
37277     },
37278
37279     adjustWidth : function(tag, w){
37280         tag = tag.toLowerCase();
37281         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37282             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37283                 if(tag == 'input'){
37284                     return w + 2;
37285                 }
37286                 if(tag == 'textarea'){
37287                     return w-2;
37288                 }
37289             }else if(Roo.isOpera){
37290                 if(tag == 'input'){
37291                     return w + 2;
37292                 }
37293                 if(tag == 'textarea'){
37294                     return w-2;
37295                 }
37296             }
37297         }
37298         return w;
37299     }
37300 });
37301
37302
37303 // anything other than normal should be considered experimental
37304 Roo.form.Field.msgFx = {
37305     normal : {
37306         show: function(msgEl, f){
37307             msgEl.setDisplayed('block');
37308         },
37309
37310         hide : function(msgEl, f){
37311             msgEl.setDisplayed(false).update('');
37312         }
37313     },
37314
37315     slide : {
37316         show: function(msgEl, f){
37317             msgEl.slideIn('t', {stopFx:true});
37318         },
37319
37320         hide : function(msgEl, f){
37321             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37322         }
37323     },
37324
37325     slideRight : {
37326         show: function(msgEl, f){
37327             msgEl.fixDisplay();
37328             msgEl.alignTo(f.el, 'tl-tr');
37329             msgEl.slideIn('l', {stopFx:true});
37330         },
37331
37332         hide : function(msgEl, f){
37333             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37334         }
37335     }
37336 };/*
37337  * Based on:
37338  * Ext JS Library 1.1.1
37339  * Copyright(c) 2006-2007, Ext JS, LLC.
37340  *
37341  * Originally Released Under LGPL - original licence link has changed is not relivant.
37342  *
37343  * Fork - LGPL
37344  * <script type="text/javascript">
37345  */
37346  
37347
37348 /**
37349  * @class Roo.form.TextField
37350  * @extends Roo.form.Field
37351  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37352  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37353  * @constructor
37354  * Creates a new TextField
37355  * @param {Object} config Configuration options
37356  */
37357 Roo.form.TextField = function(config){
37358     Roo.form.TextField.superclass.constructor.call(this, config);
37359     this.addEvents({
37360         /**
37361          * @event autosize
37362          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37363          * according to the default logic, but this event provides a hook for the developer to apply additional
37364          * logic at runtime to resize the field if needed.
37365              * @param {Roo.form.Field} this This text field
37366              * @param {Number} width The new field width
37367              */
37368         autosize : true
37369     });
37370 };
37371
37372 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37373     /**
37374      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37375      */
37376     grow : false,
37377     /**
37378      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37379      */
37380     growMin : 30,
37381     /**
37382      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37383      */
37384     growMax : 800,
37385     /**
37386      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37387      */
37388     vtype : null,
37389     /**
37390      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37391      */
37392     maskRe : null,
37393     /**
37394      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37395      */
37396     disableKeyFilter : false,
37397     /**
37398      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37399      */
37400     allowBlank : true,
37401     /**
37402      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37403      */
37404     minLength : 0,
37405     /**
37406      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37407      */
37408     maxLength : Number.MAX_VALUE,
37409     /**
37410      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37411      */
37412     minLengthText : "The minimum length for this field is {0}",
37413     /**
37414      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37415      */
37416     maxLengthText : "The maximum length for this field is {0}",
37417     /**
37418      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37419      */
37420     selectOnFocus : false,
37421     /**
37422      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37423      */
37424     blankText : "This field is required",
37425     /**
37426      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37427      * If available, this function will be called only after the basic validators all return true, and will be passed the
37428      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37429      */
37430     validator : null,
37431     /**
37432      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37433      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37434      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37435      */
37436     regex : null,
37437     /**
37438      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37439      */
37440     regexText : "",
37441     /**
37442      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37443      */
37444     emptyText : null,
37445    
37446
37447     // private
37448     initEvents : function()
37449     {
37450         if (this.emptyText) {
37451             this.el.attr('placeholder', this.emptyText);
37452         }
37453         
37454         Roo.form.TextField.superclass.initEvents.call(this);
37455         if(this.validationEvent == 'keyup'){
37456             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37457             this.el.on('keyup', this.filterValidation, this);
37458         }
37459         else if(this.validationEvent !== false){
37460             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37461         }
37462         
37463         if(this.selectOnFocus){
37464             this.on("focus", this.preFocus, this);
37465             
37466         }
37467         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37468             this.el.on("keypress", this.filterKeys, this);
37469         }
37470         if(this.grow){
37471             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37472             this.el.on("click", this.autoSize,  this);
37473         }
37474         if(this.el.is('input[type=password]') && Roo.isSafari){
37475             this.el.on('keydown', this.SafariOnKeyDown, this);
37476         }
37477     },
37478
37479     processValue : function(value){
37480         if(this.stripCharsRe){
37481             var newValue = value.replace(this.stripCharsRe, '');
37482             if(newValue !== value){
37483                 this.setRawValue(newValue);
37484                 return newValue;
37485             }
37486         }
37487         return value;
37488     },
37489
37490     filterValidation : function(e){
37491         if(!e.isNavKeyPress()){
37492             this.validationTask.delay(this.validationDelay);
37493         }
37494     },
37495
37496     // private
37497     onKeyUp : function(e){
37498         if(!e.isNavKeyPress()){
37499             this.autoSize();
37500         }
37501     },
37502
37503     /**
37504      * Resets the current field value to the originally-loaded value and clears any validation messages.
37505      *  
37506      */
37507     reset : function(){
37508         Roo.form.TextField.superclass.reset.call(this);
37509        
37510     },
37511
37512     
37513     // private
37514     preFocus : function(){
37515         
37516         if(this.selectOnFocus){
37517             this.el.dom.select();
37518         }
37519     },
37520
37521     
37522     // private
37523     filterKeys : function(e){
37524         var k = e.getKey();
37525         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37526             return;
37527         }
37528         var c = e.getCharCode(), cc = String.fromCharCode(c);
37529         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37530             return;
37531         }
37532         if(!this.maskRe.test(cc)){
37533             e.stopEvent();
37534         }
37535     },
37536
37537     setValue : function(v){
37538         
37539         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37540         
37541         this.autoSize();
37542     },
37543
37544     /**
37545      * Validates a value according to the field's validation rules and marks the field as invalid
37546      * if the validation fails
37547      * @param {Mixed} value The value to validate
37548      * @return {Boolean} True if the value is valid, else false
37549      */
37550     validateValue : function(value){
37551         if(value.length < 1)  { // if it's blank
37552              if(this.allowBlank){
37553                 this.clearInvalid();
37554                 return true;
37555              }else{
37556                 this.markInvalid(this.blankText);
37557                 return false;
37558              }
37559         }
37560         if(value.length < this.minLength){
37561             this.markInvalid(String.format(this.minLengthText, this.minLength));
37562             return false;
37563         }
37564         if(value.length > this.maxLength){
37565             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37566             return false;
37567         }
37568         if(this.vtype){
37569             var vt = Roo.form.VTypes;
37570             if(!vt[this.vtype](value, this)){
37571                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37572                 return false;
37573             }
37574         }
37575         if(typeof this.validator == "function"){
37576             var msg = this.validator(value);
37577             if(msg !== true){
37578                 this.markInvalid(msg);
37579                 return false;
37580             }
37581         }
37582         if(this.regex && !this.regex.test(value)){
37583             this.markInvalid(this.regexText);
37584             return false;
37585         }
37586         return true;
37587     },
37588
37589     /**
37590      * Selects text in this field
37591      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37592      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37593      */
37594     selectText : function(start, end){
37595         var v = this.getRawValue();
37596         if(v.length > 0){
37597             start = start === undefined ? 0 : start;
37598             end = end === undefined ? v.length : end;
37599             var d = this.el.dom;
37600             if(d.setSelectionRange){
37601                 d.setSelectionRange(start, end);
37602             }else if(d.createTextRange){
37603                 var range = d.createTextRange();
37604                 range.moveStart("character", start);
37605                 range.moveEnd("character", v.length-end);
37606                 range.select();
37607             }
37608         }
37609     },
37610
37611     /**
37612      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37613      * This only takes effect if grow = true, and fires the autosize event.
37614      */
37615     autoSize : function(){
37616         if(!this.grow || !this.rendered){
37617             return;
37618         }
37619         if(!this.metrics){
37620             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37621         }
37622         var el = this.el;
37623         var v = el.dom.value;
37624         var d = document.createElement('div');
37625         d.appendChild(document.createTextNode(v));
37626         v = d.innerHTML;
37627         d = null;
37628         v += "&#160;";
37629         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37630         this.el.setWidth(w);
37631         this.fireEvent("autosize", this, w);
37632     },
37633     
37634     // private
37635     SafariOnKeyDown : function(event)
37636     {
37637         // this is a workaround for a password hang bug on chrome/ webkit.
37638         
37639         var isSelectAll = false;
37640         
37641         if(this.el.dom.selectionEnd > 0){
37642             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37643         }
37644         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37645             event.preventDefault();
37646             this.setValue('');
37647             return;
37648         }
37649         
37650         if(isSelectAll){ // backspace and delete key
37651             
37652             event.preventDefault();
37653             // this is very hacky as keydown always get's upper case.
37654             //
37655             var cc = String.fromCharCode(event.getCharCode());
37656             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37657             
37658         }
37659         
37660         
37661     }
37662 });/*
37663  * Based on:
37664  * Ext JS Library 1.1.1
37665  * Copyright(c) 2006-2007, Ext JS, LLC.
37666  *
37667  * Originally Released Under LGPL - original licence link has changed is not relivant.
37668  *
37669  * Fork - LGPL
37670  * <script type="text/javascript">
37671  */
37672  
37673 /**
37674  * @class Roo.form.Hidden
37675  * @extends Roo.form.TextField
37676  * Simple Hidden element used on forms 
37677  * 
37678  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37679  * 
37680  * @constructor
37681  * Creates a new Hidden form element.
37682  * @param {Object} config Configuration options
37683  */
37684
37685
37686
37687 // easy hidden field...
37688 Roo.form.Hidden = function(config){
37689     Roo.form.Hidden.superclass.constructor.call(this, config);
37690 };
37691   
37692 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37693     fieldLabel:      '',
37694     inputType:      'hidden',
37695     width:          50,
37696     allowBlank:     true,
37697     labelSeparator: '',
37698     hidden:         true,
37699     itemCls :       'x-form-item-display-none'
37700
37701
37702 });
37703
37704
37705 /*
37706  * Based on:
37707  * Ext JS Library 1.1.1
37708  * Copyright(c) 2006-2007, Ext JS, LLC.
37709  *
37710  * Originally Released Under LGPL - original licence link has changed is not relivant.
37711  *
37712  * Fork - LGPL
37713  * <script type="text/javascript">
37714  */
37715  
37716 /**
37717  * @class Roo.form.TriggerField
37718  * @extends Roo.form.TextField
37719  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37720  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37721  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37722  * for which you can provide a custom implementation.  For example:
37723  * <pre><code>
37724 var trigger = new Roo.form.TriggerField();
37725 trigger.onTriggerClick = myTriggerFn;
37726 trigger.applyTo('my-field');
37727 </code></pre>
37728  *
37729  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37730  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37731  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37732  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37733  * @constructor
37734  * Create a new TriggerField.
37735  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37736  * to the base TextField)
37737  */
37738 Roo.form.TriggerField = function(config){
37739     this.mimicing = false;
37740     Roo.form.TriggerField.superclass.constructor.call(this, config);
37741 };
37742
37743 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37744     /**
37745      * @cfg {String} triggerClass A CSS class to apply to the trigger
37746      */
37747     /**
37748      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37749      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37750      */
37751     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37752     /**
37753      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37754      */
37755     hideTrigger:false,
37756
37757     /** @cfg {Boolean} grow @hide */
37758     /** @cfg {Number} growMin @hide */
37759     /** @cfg {Number} growMax @hide */
37760
37761     /**
37762      * @hide 
37763      * @method
37764      */
37765     autoSize: Roo.emptyFn,
37766     // private
37767     monitorTab : true,
37768     // private
37769     deferHeight : true,
37770
37771     
37772     actionMode : 'wrap',
37773     // private
37774     onResize : function(w, h){
37775         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37776         if(typeof w == 'number'){
37777             var x = w - this.trigger.getWidth();
37778             this.el.setWidth(this.adjustWidth('input', x));
37779             this.trigger.setStyle('left', x+'px');
37780         }
37781     },
37782
37783     // private
37784     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37785
37786     // private
37787     getResizeEl : function(){
37788         return this.wrap;
37789     },
37790
37791     // private
37792     getPositionEl : function(){
37793         return this.wrap;
37794     },
37795
37796     // private
37797     alignErrorIcon : function(){
37798         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37799     },
37800
37801     // private
37802     onRender : function(ct, position){
37803         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37804         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37805         this.trigger = this.wrap.createChild(this.triggerConfig ||
37806                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37807         if(this.hideTrigger){
37808             this.trigger.setDisplayed(false);
37809         }
37810         this.initTrigger();
37811         if(!this.width){
37812             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37813         }
37814     },
37815
37816     // private
37817     initTrigger : function(){
37818         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37819         this.trigger.addClassOnOver('x-form-trigger-over');
37820         this.trigger.addClassOnClick('x-form-trigger-click');
37821     },
37822
37823     // private
37824     onDestroy : function(){
37825         if(this.trigger){
37826             this.trigger.removeAllListeners();
37827             this.trigger.remove();
37828         }
37829         if(this.wrap){
37830             this.wrap.remove();
37831         }
37832         Roo.form.TriggerField.superclass.onDestroy.call(this);
37833     },
37834
37835     // private
37836     onFocus : function(){
37837         Roo.form.TriggerField.superclass.onFocus.call(this);
37838         if(!this.mimicing){
37839             this.wrap.addClass('x-trigger-wrap-focus');
37840             this.mimicing = true;
37841             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37842             if(this.monitorTab){
37843                 this.el.on("keydown", this.checkTab, this);
37844             }
37845         }
37846     },
37847
37848     // private
37849     checkTab : function(e){
37850         if(e.getKey() == e.TAB){
37851             this.triggerBlur();
37852         }
37853     },
37854
37855     // private
37856     onBlur : function(){
37857         // do nothing
37858     },
37859
37860     // private
37861     mimicBlur : function(e, t){
37862         if(!this.wrap.contains(t) && this.validateBlur()){
37863             this.triggerBlur();
37864         }
37865     },
37866
37867     // private
37868     triggerBlur : function(){
37869         this.mimicing = false;
37870         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37871         if(this.monitorTab){
37872             this.el.un("keydown", this.checkTab, this);
37873         }
37874         this.wrap.removeClass('x-trigger-wrap-focus');
37875         Roo.form.TriggerField.superclass.onBlur.call(this);
37876     },
37877
37878     // private
37879     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37880     validateBlur : function(e, t){
37881         return true;
37882     },
37883
37884     // private
37885     onDisable : function(){
37886         Roo.form.TriggerField.superclass.onDisable.call(this);
37887         if(this.wrap){
37888             this.wrap.addClass('x-item-disabled');
37889         }
37890     },
37891
37892     // private
37893     onEnable : function(){
37894         Roo.form.TriggerField.superclass.onEnable.call(this);
37895         if(this.wrap){
37896             this.wrap.removeClass('x-item-disabled');
37897         }
37898     },
37899
37900     // private
37901     onShow : function(){
37902         var ae = this.getActionEl();
37903         
37904         if(ae){
37905             ae.dom.style.display = '';
37906             ae.dom.style.visibility = 'visible';
37907         }
37908     },
37909
37910     // private
37911     
37912     onHide : function(){
37913         var ae = this.getActionEl();
37914         ae.dom.style.display = 'none';
37915     },
37916
37917     /**
37918      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37919      * by an implementing function.
37920      * @method
37921      * @param {EventObject} e
37922      */
37923     onTriggerClick : Roo.emptyFn
37924 });
37925
37926 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37927 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37928 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37929 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37930     initComponent : function(){
37931         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37932
37933         this.triggerConfig = {
37934             tag:'span', cls:'x-form-twin-triggers', cn:[
37935             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37936             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37937         ]};
37938     },
37939
37940     getTrigger : function(index){
37941         return this.triggers[index];
37942     },
37943
37944     initTrigger : function(){
37945         var ts = this.trigger.select('.x-form-trigger', true);
37946         this.wrap.setStyle('overflow', 'hidden');
37947         var triggerField = this;
37948         ts.each(function(t, all, index){
37949             t.hide = function(){
37950                 var w = triggerField.wrap.getWidth();
37951                 this.dom.style.display = 'none';
37952                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37953             };
37954             t.show = function(){
37955                 var w = triggerField.wrap.getWidth();
37956                 this.dom.style.display = '';
37957                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37958             };
37959             var triggerIndex = 'Trigger'+(index+1);
37960
37961             if(this['hide'+triggerIndex]){
37962                 t.dom.style.display = 'none';
37963             }
37964             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37965             t.addClassOnOver('x-form-trigger-over');
37966             t.addClassOnClick('x-form-trigger-click');
37967         }, this);
37968         this.triggers = ts.elements;
37969     },
37970
37971     onTrigger1Click : Roo.emptyFn,
37972     onTrigger2Click : Roo.emptyFn
37973 });/*
37974  * Based on:
37975  * Ext JS Library 1.1.1
37976  * Copyright(c) 2006-2007, Ext JS, LLC.
37977  *
37978  * Originally Released Under LGPL - original licence link has changed is not relivant.
37979  *
37980  * Fork - LGPL
37981  * <script type="text/javascript">
37982  */
37983  
37984 /**
37985  * @class Roo.form.TextArea
37986  * @extends Roo.form.TextField
37987  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37988  * support for auto-sizing.
37989  * @constructor
37990  * Creates a new TextArea
37991  * @param {Object} config Configuration options
37992  */
37993 Roo.form.TextArea = function(config){
37994     Roo.form.TextArea.superclass.constructor.call(this, config);
37995     // these are provided exchanges for backwards compat
37996     // minHeight/maxHeight were replaced by growMin/growMax to be
37997     // compatible with TextField growing config values
37998     if(this.minHeight !== undefined){
37999         this.growMin = this.minHeight;
38000     }
38001     if(this.maxHeight !== undefined){
38002         this.growMax = this.maxHeight;
38003     }
38004 };
38005
38006 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38007     /**
38008      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38009      */
38010     growMin : 60,
38011     /**
38012      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38013      */
38014     growMax: 1000,
38015     /**
38016      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38017      * in the field (equivalent to setting overflow: hidden, defaults to false)
38018      */
38019     preventScrollbars: false,
38020     /**
38021      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38022      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38023      */
38024
38025     // private
38026     onRender : function(ct, position){
38027         if(!this.el){
38028             this.defaultAutoCreate = {
38029                 tag: "textarea",
38030                 style:"width:300px;height:60px;",
38031                 autocomplete: "off"
38032             };
38033         }
38034         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38035         if(this.grow){
38036             this.textSizeEl = Roo.DomHelper.append(document.body, {
38037                 tag: "pre", cls: "x-form-grow-sizer"
38038             });
38039             if(this.preventScrollbars){
38040                 this.el.setStyle("overflow", "hidden");
38041             }
38042             this.el.setHeight(this.growMin);
38043         }
38044     },
38045
38046     onDestroy : function(){
38047         if(this.textSizeEl){
38048             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38049         }
38050         Roo.form.TextArea.superclass.onDestroy.call(this);
38051     },
38052
38053     // private
38054     onKeyUp : function(e){
38055         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38056             this.autoSize();
38057         }
38058     },
38059
38060     /**
38061      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38062      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38063      */
38064     autoSize : function(){
38065         if(!this.grow || !this.textSizeEl){
38066             return;
38067         }
38068         var el = this.el;
38069         var v = el.dom.value;
38070         var ts = this.textSizeEl;
38071
38072         ts.innerHTML = '';
38073         ts.appendChild(document.createTextNode(v));
38074         v = ts.innerHTML;
38075
38076         Roo.fly(ts).setWidth(this.el.getWidth());
38077         if(v.length < 1){
38078             v = "&#160;&#160;";
38079         }else{
38080             if(Roo.isIE){
38081                 v = v.replace(/\n/g, '<p>&#160;</p>');
38082             }
38083             v += "&#160;\n&#160;";
38084         }
38085         ts.innerHTML = v;
38086         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38087         if(h != this.lastHeight){
38088             this.lastHeight = h;
38089             this.el.setHeight(h);
38090             this.fireEvent("autosize", this, h);
38091         }
38092     }
38093 });/*
38094  * Based on:
38095  * Ext JS Library 1.1.1
38096  * Copyright(c) 2006-2007, Ext JS, LLC.
38097  *
38098  * Originally Released Under LGPL - original licence link has changed is not relivant.
38099  *
38100  * Fork - LGPL
38101  * <script type="text/javascript">
38102  */
38103  
38104
38105 /**
38106  * @class Roo.form.NumberField
38107  * @extends Roo.form.TextField
38108  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38109  * @constructor
38110  * Creates a new NumberField
38111  * @param {Object} config Configuration options
38112  */
38113 Roo.form.NumberField = function(config){
38114     Roo.form.NumberField.superclass.constructor.call(this, config);
38115 };
38116
38117 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38118     /**
38119      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38120      */
38121     fieldClass: "x-form-field x-form-num-field",
38122     /**
38123      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38124      */
38125     allowDecimals : true,
38126     /**
38127      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38128      */
38129     decimalSeparator : ".",
38130     /**
38131      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38132      */
38133     decimalPrecision : 2,
38134     /**
38135      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38136      */
38137     allowNegative : true,
38138     /**
38139      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38140      */
38141     minValue : Number.NEGATIVE_INFINITY,
38142     /**
38143      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38144      */
38145     maxValue : Number.MAX_VALUE,
38146     /**
38147      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38148      */
38149     minText : "The minimum value for this field is {0}",
38150     /**
38151      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38152      */
38153     maxText : "The maximum value for this field is {0}",
38154     /**
38155      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38156      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38157      */
38158     nanText : "{0} is not a valid number",
38159
38160     // private
38161     initEvents : function(){
38162         Roo.form.NumberField.superclass.initEvents.call(this);
38163         var allowed = "0123456789";
38164         if(this.allowDecimals){
38165             allowed += this.decimalSeparator;
38166         }
38167         if(this.allowNegative){
38168             allowed += "-";
38169         }
38170         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38171         var keyPress = function(e){
38172             var k = e.getKey();
38173             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38174                 return;
38175             }
38176             var c = e.getCharCode();
38177             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38178                 e.stopEvent();
38179             }
38180         };
38181         this.el.on("keypress", keyPress, this);
38182     },
38183
38184     // private
38185     validateValue : function(value){
38186         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38187             return false;
38188         }
38189         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38190              return true;
38191         }
38192         var num = this.parseValue(value);
38193         if(isNaN(num)){
38194             this.markInvalid(String.format(this.nanText, value));
38195             return false;
38196         }
38197         if(num < this.minValue){
38198             this.markInvalid(String.format(this.minText, this.minValue));
38199             return false;
38200         }
38201         if(num > this.maxValue){
38202             this.markInvalid(String.format(this.maxText, this.maxValue));
38203             return false;
38204         }
38205         return true;
38206     },
38207
38208     getValue : function(){
38209         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38210     },
38211
38212     // private
38213     parseValue : function(value){
38214         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38215         return isNaN(value) ? '' : value;
38216     },
38217
38218     // private
38219     fixPrecision : function(value){
38220         var nan = isNaN(value);
38221         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38222             return nan ? '' : value;
38223         }
38224         return parseFloat(value).toFixed(this.decimalPrecision);
38225     },
38226
38227     setValue : function(v){
38228         v = this.fixPrecision(v);
38229         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38230     },
38231
38232     // private
38233     decimalPrecisionFcn : function(v){
38234         return Math.floor(v);
38235     },
38236
38237     beforeBlur : function(){
38238         var v = this.parseValue(this.getRawValue());
38239         if(v){
38240             this.setValue(v);
38241         }
38242     }
38243 });/*
38244  * Based on:
38245  * Ext JS Library 1.1.1
38246  * Copyright(c) 2006-2007, Ext JS, LLC.
38247  *
38248  * Originally Released Under LGPL - original licence link has changed is not relivant.
38249  *
38250  * Fork - LGPL
38251  * <script type="text/javascript">
38252  */
38253  
38254 /**
38255  * @class Roo.form.DateField
38256  * @extends Roo.form.TriggerField
38257  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38258 * @constructor
38259 * Create a new DateField
38260 * @param {Object} config
38261  */
38262 Roo.form.DateField = function(config){
38263     Roo.form.DateField.superclass.constructor.call(this, config);
38264     
38265       this.addEvents({
38266          
38267         /**
38268          * @event select
38269          * Fires when a date is selected
38270              * @param {Roo.form.DateField} combo This combo box
38271              * @param {Date} date The date selected
38272              */
38273         'select' : true
38274          
38275     });
38276     
38277     
38278     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38279     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38280     this.ddMatch = null;
38281     if(this.disabledDates){
38282         var dd = this.disabledDates;
38283         var re = "(?:";
38284         for(var i = 0; i < dd.length; i++){
38285             re += dd[i];
38286             if(i != dd.length-1) re += "|";
38287         }
38288         this.ddMatch = new RegExp(re + ")");
38289     }
38290 };
38291
38292 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38293     /**
38294      * @cfg {String} format
38295      * The default date format string which can be overriden for localization support.  The format must be
38296      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38297      */
38298     format : "m/d/y",
38299     /**
38300      * @cfg {String} altFormats
38301      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38302      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38303      */
38304     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38305     /**
38306      * @cfg {Array} disabledDays
38307      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38308      */
38309     disabledDays : null,
38310     /**
38311      * @cfg {String} disabledDaysText
38312      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38313      */
38314     disabledDaysText : "Disabled",
38315     /**
38316      * @cfg {Array} disabledDates
38317      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38318      * expression so they are very powerful. Some examples:
38319      * <ul>
38320      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38321      * <li>["03/08", "09/16"] would disable those days for every year</li>
38322      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38323      * <li>["03/../2006"] would disable every day in March 2006</li>
38324      * <li>["^03"] would disable every day in every March</li>
38325      * </ul>
38326      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38327      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38328      */
38329     disabledDates : null,
38330     /**
38331      * @cfg {String} disabledDatesText
38332      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38333      */
38334     disabledDatesText : "Disabled",
38335     /**
38336      * @cfg {Date/String} minValue
38337      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38338      * valid format (defaults to null).
38339      */
38340     minValue : null,
38341     /**
38342      * @cfg {Date/String} maxValue
38343      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38344      * valid format (defaults to null).
38345      */
38346     maxValue : null,
38347     /**
38348      * @cfg {String} minText
38349      * The error text to display when the date in the cell is before minValue (defaults to
38350      * 'The date in this field must be after {minValue}').
38351      */
38352     minText : "The date in this field must be equal to or after {0}",
38353     /**
38354      * @cfg {String} maxText
38355      * The error text to display when the date in the cell is after maxValue (defaults to
38356      * 'The date in this field must be before {maxValue}').
38357      */
38358     maxText : "The date in this field must be equal to or before {0}",
38359     /**
38360      * @cfg {String} invalidText
38361      * The error text to display when the date in the field is invalid (defaults to
38362      * '{value} is not a valid date - it must be in the format {format}').
38363      */
38364     invalidText : "{0} is not a valid date - it must be in the format {1}",
38365     /**
38366      * @cfg {String} triggerClass
38367      * An additional CSS class used to style the trigger button.  The trigger will always get the
38368      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38369      * which displays a calendar icon).
38370      */
38371     triggerClass : 'x-form-date-trigger',
38372     
38373
38374     /**
38375      * @cfg {Boolean} useIso
38376      * if enabled, then the date field will use a hidden field to store the 
38377      * real value as iso formated date. default (false)
38378      */ 
38379     useIso : false,
38380     /**
38381      * @cfg {String/Object} autoCreate
38382      * A DomHelper element spec, or true for a default element spec (defaults to
38383      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38384      */ 
38385     // private
38386     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38387     
38388     // private
38389     hiddenField: false,
38390     
38391     onRender : function(ct, position)
38392     {
38393         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38394         if (this.useIso) {
38395             //this.el.dom.removeAttribute('name'); 
38396             Roo.log("Changing name?");
38397             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38398             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38399                     'before', true);
38400             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38401             // prevent input submission
38402             this.hiddenName = this.name;
38403         }
38404             
38405             
38406     },
38407     
38408     // private
38409     validateValue : function(value)
38410     {
38411         value = this.formatDate(value);
38412         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38413             Roo.log('super failed');
38414             return false;
38415         }
38416         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38417              return true;
38418         }
38419         var svalue = value;
38420         value = this.parseDate(value);
38421         if(!value){
38422             Roo.log('parse date failed' + svalue);
38423             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38424             return false;
38425         }
38426         var time = value.getTime();
38427         if(this.minValue && time < this.minValue.getTime()){
38428             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38429             return false;
38430         }
38431         if(this.maxValue && time > this.maxValue.getTime()){
38432             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38433             return false;
38434         }
38435         if(this.disabledDays){
38436             var day = value.getDay();
38437             for(var i = 0; i < this.disabledDays.length; i++) {
38438                 if(day === this.disabledDays[i]){
38439                     this.markInvalid(this.disabledDaysText);
38440                     return false;
38441                 }
38442             }
38443         }
38444         var fvalue = this.formatDate(value);
38445         if(this.ddMatch && this.ddMatch.test(fvalue)){
38446             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38447             return false;
38448         }
38449         return true;
38450     },
38451
38452     // private
38453     // Provides logic to override the default TriggerField.validateBlur which just returns true
38454     validateBlur : function(){
38455         return !this.menu || !this.menu.isVisible();
38456     },
38457     
38458     getName: function()
38459     {
38460         // returns hidden if it's set..
38461         if (!this.rendered) {return ''};
38462         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38463         
38464     },
38465
38466     /**
38467      * Returns the current date value of the date field.
38468      * @return {Date} The date value
38469      */
38470     getValue : function(){
38471         
38472         return  this.hiddenField ?
38473                 this.hiddenField.value :
38474                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38475     },
38476
38477     /**
38478      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38479      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38480      * (the default format used is "m/d/y").
38481      * <br />Usage:
38482      * <pre><code>
38483 //All of these calls set the same date value (May 4, 2006)
38484
38485 //Pass a date object:
38486 var dt = new Date('5/4/06');
38487 dateField.setValue(dt);
38488
38489 //Pass a date string (default format):
38490 dateField.setValue('5/4/06');
38491
38492 //Pass a date string (custom format):
38493 dateField.format = 'Y-m-d';
38494 dateField.setValue('2006-5-4');
38495 </code></pre>
38496      * @param {String/Date} date The date or valid date string
38497      */
38498     setValue : function(date){
38499         if (this.hiddenField) {
38500             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38501         }
38502         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38503         // make sure the value field is always stored as a date..
38504         this.value = this.parseDate(date);
38505         
38506         
38507     },
38508
38509     // private
38510     parseDate : function(value){
38511         if(!value || value instanceof Date){
38512             return value;
38513         }
38514         var v = Date.parseDate(value, this.format);
38515          if (!v && this.useIso) {
38516             v = Date.parseDate(value, 'Y-m-d');
38517         }
38518         if(!v && this.altFormats){
38519             if(!this.altFormatsArray){
38520                 this.altFormatsArray = this.altFormats.split("|");
38521             }
38522             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38523                 v = Date.parseDate(value, this.altFormatsArray[i]);
38524             }
38525         }
38526         return v;
38527     },
38528
38529     // private
38530     formatDate : function(date, fmt){
38531         return (!date || !(date instanceof Date)) ?
38532                date : date.dateFormat(fmt || this.format);
38533     },
38534
38535     // private
38536     menuListeners : {
38537         select: function(m, d){
38538             
38539             this.setValue(d);
38540             this.fireEvent('select', this, d);
38541         },
38542         show : function(){ // retain focus styling
38543             this.onFocus();
38544         },
38545         hide : function(){
38546             this.focus.defer(10, this);
38547             var ml = this.menuListeners;
38548             this.menu.un("select", ml.select,  this);
38549             this.menu.un("show", ml.show,  this);
38550             this.menu.un("hide", ml.hide,  this);
38551         }
38552     },
38553
38554     // private
38555     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38556     onTriggerClick : function(){
38557         if(this.disabled){
38558             return;
38559         }
38560         if(this.menu == null){
38561             this.menu = new Roo.menu.DateMenu();
38562         }
38563         Roo.apply(this.menu.picker,  {
38564             showClear: this.allowBlank,
38565             minDate : this.minValue,
38566             maxDate : this.maxValue,
38567             disabledDatesRE : this.ddMatch,
38568             disabledDatesText : this.disabledDatesText,
38569             disabledDays : this.disabledDays,
38570             disabledDaysText : this.disabledDaysText,
38571             format : this.useIso ? 'Y-m-d' : this.format,
38572             minText : String.format(this.minText, this.formatDate(this.minValue)),
38573             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38574         });
38575         this.menu.on(Roo.apply({}, this.menuListeners, {
38576             scope:this
38577         }));
38578         this.menu.picker.setValue(this.getValue() || new Date());
38579         this.menu.show(this.el, "tl-bl?");
38580     },
38581
38582     beforeBlur : function(){
38583         var v = this.parseDate(this.getRawValue());
38584         if(v){
38585             this.setValue(v);
38586         }
38587     },
38588
38589     /*@
38590      * overide
38591      * 
38592      */
38593     isDirty : function() {
38594         if(this.disabled) {
38595             return false;
38596         }
38597         
38598         if(typeof(this.startValue) === 'undefined'){
38599             return false;
38600         }
38601         
38602         return String(this.getValue()) !== String(this.startValue);
38603         
38604     }
38605 });/*
38606  * Based on:
38607  * Ext JS Library 1.1.1
38608  * Copyright(c) 2006-2007, Ext JS, LLC.
38609  *
38610  * Originally Released Under LGPL - original licence link has changed is not relivant.
38611  *
38612  * Fork - LGPL
38613  * <script type="text/javascript">
38614  */
38615  
38616 /**
38617  * @class Roo.form.MonthField
38618  * @extends Roo.form.TriggerField
38619  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38620 * @constructor
38621 * Create a new MonthField
38622 * @param {Object} config
38623  */
38624 Roo.form.MonthField = function(config){
38625     
38626     Roo.form.MonthField.superclass.constructor.call(this, config);
38627     
38628       this.addEvents({
38629          
38630         /**
38631          * @event select
38632          * Fires when a date is selected
38633              * @param {Roo.form.MonthFieeld} combo This combo box
38634              * @param {Date} date The date selected
38635              */
38636         'select' : true
38637          
38638     });
38639     
38640     
38641     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38642     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38643     this.ddMatch = null;
38644     if(this.disabledDates){
38645         var dd = this.disabledDates;
38646         var re = "(?:";
38647         for(var i = 0; i < dd.length; i++){
38648             re += dd[i];
38649             if(i != dd.length-1) re += "|";
38650         }
38651         this.ddMatch = new RegExp(re + ")");
38652     }
38653 };
38654
38655 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38656     /**
38657      * @cfg {String} format
38658      * The default date format string which can be overriden for localization support.  The format must be
38659      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38660      */
38661     format : "M Y",
38662     /**
38663      * @cfg {String} altFormats
38664      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38665      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38666      */
38667     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38668     /**
38669      * @cfg {Array} disabledDays
38670      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38671      */
38672     disabledDays : [0,1,2,3,4,5,6],
38673     /**
38674      * @cfg {String} disabledDaysText
38675      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38676      */
38677     disabledDaysText : "Disabled",
38678     /**
38679      * @cfg {Array} disabledDates
38680      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38681      * expression so they are very powerful. Some examples:
38682      * <ul>
38683      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38684      * <li>["03/08", "09/16"] would disable those days for every year</li>
38685      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38686      * <li>["03/../2006"] would disable every day in March 2006</li>
38687      * <li>["^03"] would disable every day in every March</li>
38688      * </ul>
38689      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38690      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38691      */
38692     disabledDates : null,
38693     /**
38694      * @cfg {String} disabledDatesText
38695      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38696      */
38697     disabledDatesText : "Disabled",
38698     /**
38699      * @cfg {Date/String} minValue
38700      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38701      * valid format (defaults to null).
38702      */
38703     minValue : null,
38704     /**
38705      * @cfg {Date/String} maxValue
38706      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38707      * valid format (defaults to null).
38708      */
38709     maxValue : null,
38710     /**
38711      * @cfg {String} minText
38712      * The error text to display when the date in the cell is before minValue (defaults to
38713      * 'The date in this field must be after {minValue}').
38714      */
38715     minText : "The date in this field must be equal to or after {0}",
38716     /**
38717      * @cfg {String} maxTextf
38718      * The error text to display when the date in the cell is after maxValue (defaults to
38719      * 'The date in this field must be before {maxValue}').
38720      */
38721     maxText : "The date in this field must be equal to or before {0}",
38722     /**
38723      * @cfg {String} invalidText
38724      * The error text to display when the date in the field is invalid (defaults to
38725      * '{value} is not a valid date - it must be in the format {format}').
38726      */
38727     invalidText : "{0} is not a valid date - it must be in the format {1}",
38728     /**
38729      * @cfg {String} triggerClass
38730      * An additional CSS class used to style the trigger button.  The trigger will always get the
38731      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38732      * which displays a calendar icon).
38733      */
38734     triggerClass : 'x-form-date-trigger',
38735     
38736
38737     /**
38738      * @cfg {Boolean} useIso
38739      * if enabled, then the date field will use a hidden field to store the 
38740      * real value as iso formated date. default (true)
38741      */ 
38742     useIso : true,
38743     /**
38744      * @cfg {String/Object} autoCreate
38745      * A DomHelper element spec, or true for a default element spec (defaults to
38746      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38747      */ 
38748     // private
38749     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38750     
38751     // private
38752     hiddenField: false,
38753     
38754     hideMonthPicker : false,
38755     
38756     onRender : function(ct, position)
38757     {
38758         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38759         if (this.useIso) {
38760             this.el.dom.removeAttribute('name'); 
38761             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38762                     'before', true);
38763             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38764             // prevent input submission
38765             this.hiddenName = this.name;
38766         }
38767             
38768             
38769     },
38770     
38771     // private
38772     validateValue : function(value)
38773     {
38774         value = this.formatDate(value);
38775         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38776             return false;
38777         }
38778         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38779              return true;
38780         }
38781         var svalue = value;
38782         value = this.parseDate(value);
38783         if(!value){
38784             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38785             return false;
38786         }
38787         var time = value.getTime();
38788         if(this.minValue && time < this.minValue.getTime()){
38789             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38790             return false;
38791         }
38792         if(this.maxValue && time > this.maxValue.getTime()){
38793             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38794             return false;
38795         }
38796         /*if(this.disabledDays){
38797             var day = value.getDay();
38798             for(var i = 0; i < this.disabledDays.length; i++) {
38799                 if(day === this.disabledDays[i]){
38800                     this.markInvalid(this.disabledDaysText);
38801                     return false;
38802                 }
38803             }
38804         }
38805         */
38806         var fvalue = this.formatDate(value);
38807         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38808             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38809             return false;
38810         }
38811         */
38812         return true;
38813     },
38814
38815     // private
38816     // Provides logic to override the default TriggerField.validateBlur which just returns true
38817     validateBlur : function(){
38818         return !this.menu || !this.menu.isVisible();
38819     },
38820
38821     /**
38822      * Returns the current date value of the date field.
38823      * @return {Date} The date value
38824      */
38825     getValue : function(){
38826         
38827         
38828         
38829         return  this.hiddenField ?
38830                 this.hiddenField.value :
38831                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38832     },
38833
38834     /**
38835      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38836      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38837      * (the default format used is "m/d/y").
38838      * <br />Usage:
38839      * <pre><code>
38840 //All of these calls set the same date value (May 4, 2006)
38841
38842 //Pass a date object:
38843 var dt = new Date('5/4/06');
38844 monthField.setValue(dt);
38845
38846 //Pass a date string (default format):
38847 monthField.setValue('5/4/06');
38848
38849 //Pass a date string (custom format):
38850 monthField.format = 'Y-m-d';
38851 monthField.setValue('2006-5-4');
38852 </code></pre>
38853      * @param {String/Date} date The date or valid date string
38854      */
38855     setValue : function(date){
38856         Roo.log('month setValue' + date);
38857         // can only be first of month..
38858         
38859         var val = this.parseDate(date);
38860         
38861         if (this.hiddenField) {
38862             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38863         }
38864         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38865         this.value = this.parseDate(date);
38866     },
38867
38868     // private
38869     parseDate : function(value){
38870         if(!value || value instanceof Date){
38871             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38872             return value;
38873         }
38874         var v = Date.parseDate(value, this.format);
38875         if (!v && this.useIso) {
38876             v = Date.parseDate(value, 'Y-m-d');
38877         }
38878         if (v) {
38879             // 
38880             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38881         }
38882         
38883         
38884         if(!v && this.altFormats){
38885             if(!this.altFormatsArray){
38886                 this.altFormatsArray = this.altFormats.split("|");
38887             }
38888             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38889                 v = Date.parseDate(value, this.altFormatsArray[i]);
38890             }
38891         }
38892         return v;
38893     },
38894
38895     // private
38896     formatDate : function(date, fmt){
38897         return (!date || !(date instanceof Date)) ?
38898                date : date.dateFormat(fmt || this.format);
38899     },
38900
38901     // private
38902     menuListeners : {
38903         select: function(m, d){
38904             this.setValue(d);
38905             this.fireEvent('select', this, d);
38906         },
38907         show : function(){ // retain focus styling
38908             this.onFocus();
38909         },
38910         hide : function(){
38911             this.focus.defer(10, this);
38912             var ml = this.menuListeners;
38913             this.menu.un("select", ml.select,  this);
38914             this.menu.un("show", ml.show,  this);
38915             this.menu.un("hide", ml.hide,  this);
38916         }
38917     },
38918     // private
38919     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38920     onTriggerClick : function(){
38921         if(this.disabled){
38922             return;
38923         }
38924         if(this.menu == null){
38925             this.menu = new Roo.menu.DateMenu();
38926            
38927         }
38928         
38929         Roo.apply(this.menu.picker,  {
38930             
38931             showClear: this.allowBlank,
38932             minDate : this.minValue,
38933             maxDate : this.maxValue,
38934             disabledDatesRE : this.ddMatch,
38935             disabledDatesText : this.disabledDatesText,
38936             
38937             format : this.useIso ? 'Y-m-d' : this.format,
38938             minText : String.format(this.minText, this.formatDate(this.minValue)),
38939             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38940             
38941         });
38942          this.menu.on(Roo.apply({}, this.menuListeners, {
38943             scope:this
38944         }));
38945        
38946         
38947         var m = this.menu;
38948         var p = m.picker;
38949         
38950         // hide month picker get's called when we called by 'before hide';
38951         
38952         var ignorehide = true;
38953         p.hideMonthPicker  = function(disableAnim){
38954             if (ignorehide) {
38955                 return;
38956             }
38957              if(this.monthPicker){
38958                 Roo.log("hideMonthPicker called");
38959                 if(disableAnim === true){
38960                     this.monthPicker.hide();
38961                 }else{
38962                     this.monthPicker.slideOut('t', {duration:.2});
38963                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38964                     p.fireEvent("select", this, this.value);
38965                     m.hide();
38966                 }
38967             }
38968         }
38969         
38970         Roo.log('picker set value');
38971         Roo.log(this.getValue());
38972         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38973         m.show(this.el, 'tl-bl?');
38974         ignorehide  = false;
38975         // this will trigger hideMonthPicker..
38976         
38977         
38978         // hidden the day picker
38979         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38980         
38981         
38982         
38983       
38984         
38985         p.showMonthPicker.defer(100, p);
38986     
38987         
38988        
38989     },
38990
38991     beforeBlur : function(){
38992         var v = this.parseDate(this.getRawValue());
38993         if(v){
38994             this.setValue(v);
38995         }
38996     }
38997
38998     /** @cfg {Boolean} grow @hide */
38999     /** @cfg {Number} growMin @hide */
39000     /** @cfg {Number} growMax @hide */
39001     /**
39002      * @hide
39003      * @method autoSize
39004      */
39005 });/*
39006  * Based on:
39007  * Ext JS Library 1.1.1
39008  * Copyright(c) 2006-2007, Ext JS, LLC.
39009  *
39010  * Originally Released Under LGPL - original licence link has changed is not relivant.
39011  *
39012  * Fork - LGPL
39013  * <script type="text/javascript">
39014  */
39015  
39016
39017 /**
39018  * @class Roo.form.ComboBox
39019  * @extends Roo.form.TriggerField
39020  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39021  * @constructor
39022  * Create a new ComboBox.
39023  * @param {Object} config Configuration options
39024  */
39025 Roo.form.ComboBox = function(config){
39026     Roo.form.ComboBox.superclass.constructor.call(this, config);
39027     this.addEvents({
39028         /**
39029          * @event expand
39030          * Fires when the dropdown list is expanded
39031              * @param {Roo.form.ComboBox} combo This combo box
39032              */
39033         'expand' : true,
39034         /**
39035          * @event collapse
39036          * Fires when the dropdown list is collapsed
39037              * @param {Roo.form.ComboBox} combo This combo box
39038              */
39039         'collapse' : true,
39040         /**
39041          * @event beforeselect
39042          * Fires before a list item is selected. Return false to cancel the selection.
39043              * @param {Roo.form.ComboBox} combo This combo box
39044              * @param {Roo.data.Record} record The data record returned from the underlying store
39045              * @param {Number} index The index of the selected item in the dropdown list
39046              */
39047         'beforeselect' : true,
39048         /**
39049          * @event select
39050          * Fires when a list item is selected
39051              * @param {Roo.form.ComboBox} combo This combo box
39052              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39053              * @param {Number} index The index of the selected item in the dropdown list
39054              */
39055         'select' : true,
39056         /**
39057          * @event beforequery
39058          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39059          * The event object passed has these properties:
39060              * @param {Roo.form.ComboBox} combo This combo box
39061              * @param {String} query The query
39062              * @param {Boolean} forceAll true to force "all" query
39063              * @param {Boolean} cancel true to cancel the query
39064              * @param {Object} e The query event object
39065              */
39066         'beforequery': true,
39067          /**
39068          * @event add
39069          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39070              * @param {Roo.form.ComboBox} combo This combo box
39071              */
39072         'add' : true,
39073         /**
39074          * @event edit
39075          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39076              * @param {Roo.form.ComboBox} combo This combo box
39077              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39078              */
39079         'edit' : true
39080         
39081         
39082     });
39083     if(this.transform){
39084         this.allowDomMove = false;
39085         var s = Roo.getDom(this.transform);
39086         if(!this.hiddenName){
39087             this.hiddenName = s.name;
39088         }
39089         if(!this.store){
39090             this.mode = 'local';
39091             var d = [], opts = s.options;
39092             for(var i = 0, len = opts.length;i < len; i++){
39093                 var o = opts[i];
39094                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39095                 if(o.selected) {
39096                     this.value = value;
39097                 }
39098                 d.push([value, o.text]);
39099             }
39100             this.store = new Roo.data.SimpleStore({
39101                 'id': 0,
39102                 fields: ['value', 'text'],
39103                 data : d
39104             });
39105             this.valueField = 'value';
39106             this.displayField = 'text';
39107         }
39108         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39109         if(!this.lazyRender){
39110             this.target = true;
39111             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39112             s.parentNode.removeChild(s); // remove it
39113             this.render(this.el.parentNode);
39114         }else{
39115             s.parentNode.removeChild(s); // remove it
39116         }
39117
39118     }
39119     if (this.store) {
39120         this.store = Roo.factory(this.store, Roo.data);
39121     }
39122     
39123     this.selectedIndex = -1;
39124     if(this.mode == 'local'){
39125         if(config.queryDelay === undefined){
39126             this.queryDelay = 10;
39127         }
39128         if(config.minChars === undefined){
39129             this.minChars = 0;
39130         }
39131     }
39132 };
39133
39134 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39135     /**
39136      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39137      */
39138     /**
39139      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39140      * rendering into an Roo.Editor, defaults to false)
39141      */
39142     /**
39143      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39144      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39145      */
39146     /**
39147      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39148      */
39149     /**
39150      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39151      * the dropdown list (defaults to undefined, with no header element)
39152      */
39153
39154      /**
39155      * @cfg {String/Roo.Template} tpl The template to use to render the output
39156      */
39157      
39158     // private
39159     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39160     /**
39161      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39162      */
39163     listWidth: undefined,
39164     /**
39165      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39166      * mode = 'remote' or 'text' if mode = 'local')
39167      */
39168     displayField: undefined,
39169     /**
39170      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39171      * mode = 'remote' or 'value' if mode = 'local'). 
39172      * Note: use of a valueField requires the user make a selection
39173      * in order for a value to be mapped.
39174      */
39175     valueField: undefined,
39176     
39177     
39178     /**
39179      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39180      * field's data value (defaults to the underlying DOM element's name)
39181      */
39182     hiddenName: undefined,
39183     /**
39184      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39185      */
39186     listClass: '',
39187     /**
39188      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39189      */
39190     selectedClass: 'x-combo-selected',
39191     /**
39192      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39193      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39194      * which displays a downward arrow icon).
39195      */
39196     triggerClass : 'x-form-arrow-trigger',
39197     /**
39198      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39199      */
39200     shadow:'sides',
39201     /**
39202      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39203      * anchor positions (defaults to 'tl-bl')
39204      */
39205     listAlign: 'tl-bl?',
39206     /**
39207      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39208      */
39209     maxHeight: 300,
39210     /**
39211      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39212      * query specified by the allQuery config option (defaults to 'query')
39213      */
39214     triggerAction: 'query',
39215     /**
39216      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39217      * (defaults to 4, does not apply if editable = false)
39218      */
39219     minChars : 4,
39220     /**
39221      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39222      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39223      */
39224     typeAhead: false,
39225     /**
39226      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39227      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39228      */
39229     queryDelay: 500,
39230     /**
39231      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39232      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39233      */
39234     pageSize: 0,
39235     /**
39236      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39237      * when editable = true (defaults to false)
39238      */
39239     selectOnFocus:false,
39240     /**
39241      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39242      */
39243     queryParam: 'query',
39244     /**
39245      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39246      * when mode = 'remote' (defaults to 'Loading...')
39247      */
39248     loadingText: 'Loading...',
39249     /**
39250      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39251      */
39252     resizable: false,
39253     /**
39254      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39255      */
39256     handleHeight : 8,
39257     /**
39258      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39259      * traditional select (defaults to true)
39260      */
39261     editable: true,
39262     /**
39263      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39264      */
39265     allQuery: '',
39266     /**
39267      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39268      */
39269     mode: 'remote',
39270     /**
39271      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39272      * listWidth has a higher value)
39273      */
39274     minListWidth : 70,
39275     /**
39276      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39277      * allow the user to set arbitrary text into the field (defaults to false)
39278      */
39279     forceSelection:false,
39280     /**
39281      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39282      * if typeAhead = true (defaults to 250)
39283      */
39284     typeAheadDelay : 250,
39285     /**
39286      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39287      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39288      */
39289     valueNotFoundText : undefined,
39290     /**
39291      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39292      */
39293     blockFocus : false,
39294     
39295     /**
39296      * @cfg {Boolean} disableClear Disable showing of clear button.
39297      */
39298     disableClear : false,
39299     /**
39300      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39301      */
39302     alwaysQuery : false,
39303     
39304     //private
39305     addicon : false,
39306     editicon: false,
39307     
39308     // element that contains real text value.. (when hidden is used..)
39309      
39310     // private
39311     onRender : function(ct, position){
39312         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39313         if(this.hiddenName){
39314             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39315                     'before', true);
39316             this.hiddenField.value =
39317                 this.hiddenValue !== undefined ? this.hiddenValue :
39318                 this.value !== undefined ? this.value : '';
39319
39320             // prevent input submission
39321             this.el.dom.removeAttribute('name');
39322              
39323              
39324         }
39325         if(Roo.isGecko){
39326             this.el.dom.setAttribute('autocomplete', 'off');
39327         }
39328
39329         var cls = 'x-combo-list';
39330
39331         this.list = new Roo.Layer({
39332             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39333         });
39334
39335         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39336         this.list.setWidth(lw);
39337         this.list.swallowEvent('mousewheel');
39338         this.assetHeight = 0;
39339
39340         if(this.title){
39341             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39342             this.assetHeight += this.header.getHeight();
39343         }
39344
39345         this.innerList = this.list.createChild({cls:cls+'-inner'});
39346         this.innerList.on('mouseover', this.onViewOver, this);
39347         this.innerList.on('mousemove', this.onViewMove, this);
39348         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39349         
39350         if(this.allowBlank && !this.pageSize && !this.disableClear){
39351             this.footer = this.list.createChild({cls:cls+'-ft'});
39352             this.pageTb = new Roo.Toolbar(this.footer);
39353            
39354         }
39355         if(this.pageSize){
39356             this.footer = this.list.createChild({cls:cls+'-ft'});
39357             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39358                     {pageSize: this.pageSize});
39359             
39360         }
39361         
39362         if (this.pageTb && this.allowBlank && !this.disableClear) {
39363             var _this = this;
39364             this.pageTb.add(new Roo.Toolbar.Fill(), {
39365                 cls: 'x-btn-icon x-btn-clear',
39366                 text: '&#160;',
39367                 handler: function()
39368                 {
39369                     _this.collapse();
39370                     _this.clearValue();
39371                     _this.onSelect(false, -1);
39372                 }
39373             });
39374         }
39375         if (this.footer) {
39376             this.assetHeight += this.footer.getHeight();
39377         }
39378         
39379
39380         if(!this.tpl){
39381             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39382         }
39383
39384         this.view = new Roo.View(this.innerList, this.tpl, {
39385             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39386         });
39387
39388         this.view.on('click', this.onViewClick, this);
39389
39390         this.store.on('beforeload', this.onBeforeLoad, this);
39391         this.store.on('load', this.onLoad, this);
39392         this.store.on('loadexception', this.onLoadException, this);
39393
39394         if(this.resizable){
39395             this.resizer = new Roo.Resizable(this.list,  {
39396                pinned:true, handles:'se'
39397             });
39398             this.resizer.on('resize', function(r, w, h){
39399                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39400                 this.listWidth = w;
39401                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39402                 this.restrictHeight();
39403             }, this);
39404             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39405         }
39406         if(!this.editable){
39407             this.editable = true;
39408             this.setEditable(false);
39409         }  
39410         
39411         
39412         if (typeof(this.events.add.listeners) != 'undefined') {
39413             
39414             this.addicon = this.wrap.createChild(
39415                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39416        
39417             this.addicon.on('click', function(e) {
39418                 this.fireEvent('add', this);
39419             }, this);
39420         }
39421         if (typeof(this.events.edit.listeners) != 'undefined') {
39422             
39423             this.editicon = this.wrap.createChild(
39424                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39425             if (this.addicon) {
39426                 this.editicon.setStyle('margin-left', '40px');
39427             }
39428             this.editicon.on('click', function(e) {
39429                 
39430                 // we fire even  if inothing is selected..
39431                 this.fireEvent('edit', this, this.lastData );
39432                 
39433             }, this);
39434         }
39435         
39436         
39437         
39438     },
39439
39440     // private
39441     initEvents : function(){
39442         Roo.form.ComboBox.superclass.initEvents.call(this);
39443
39444         this.keyNav = new Roo.KeyNav(this.el, {
39445             "up" : function(e){
39446                 this.inKeyMode = true;
39447                 this.selectPrev();
39448             },
39449
39450             "down" : function(e){
39451                 if(!this.isExpanded()){
39452                     this.onTriggerClick();
39453                 }else{
39454                     this.inKeyMode = true;
39455                     this.selectNext();
39456                 }
39457             },
39458
39459             "enter" : function(e){
39460                 this.onViewClick();
39461                 //return true;
39462             },
39463
39464             "esc" : function(e){
39465                 this.collapse();
39466             },
39467
39468             "tab" : function(e){
39469                 this.onViewClick(false);
39470                 this.fireEvent("specialkey", this, e);
39471                 return true;
39472             },
39473
39474             scope : this,
39475
39476             doRelay : function(foo, bar, hname){
39477                 if(hname == 'down' || this.scope.isExpanded()){
39478                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39479                 }
39480                 return true;
39481             },
39482
39483             forceKeyDown: true
39484         });
39485         this.queryDelay = Math.max(this.queryDelay || 10,
39486                 this.mode == 'local' ? 10 : 250);
39487         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39488         if(this.typeAhead){
39489             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39490         }
39491         if(this.editable !== false){
39492             this.el.on("keyup", this.onKeyUp, this);
39493         }
39494         if(this.forceSelection){
39495             this.on('blur', this.doForce, this);
39496         }
39497     },
39498
39499     onDestroy : function(){
39500         if(this.view){
39501             this.view.setStore(null);
39502             this.view.el.removeAllListeners();
39503             this.view.el.remove();
39504             this.view.purgeListeners();
39505         }
39506         if(this.list){
39507             this.list.destroy();
39508         }
39509         if(this.store){
39510             this.store.un('beforeload', this.onBeforeLoad, this);
39511             this.store.un('load', this.onLoad, this);
39512             this.store.un('loadexception', this.onLoadException, this);
39513         }
39514         Roo.form.ComboBox.superclass.onDestroy.call(this);
39515     },
39516
39517     // private
39518     fireKey : function(e){
39519         if(e.isNavKeyPress() && !this.list.isVisible()){
39520             this.fireEvent("specialkey", this, e);
39521         }
39522     },
39523
39524     // private
39525     onResize: function(w, h){
39526         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39527         
39528         if(typeof w != 'number'){
39529             // we do not handle it!?!?
39530             return;
39531         }
39532         var tw = this.trigger.getWidth();
39533         tw += this.addicon ? this.addicon.getWidth() : 0;
39534         tw += this.editicon ? this.editicon.getWidth() : 0;
39535         var x = w - tw;
39536         this.el.setWidth( this.adjustWidth('input', x));
39537             
39538         this.trigger.setStyle('left', x+'px');
39539         
39540         if(this.list && this.listWidth === undefined){
39541             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39542             this.list.setWidth(lw);
39543             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39544         }
39545         
39546     
39547         
39548     },
39549
39550     /**
39551      * Allow or prevent the user from directly editing the field text.  If false is passed,
39552      * the user will only be able to select from the items defined in the dropdown list.  This method
39553      * is the runtime equivalent of setting the 'editable' config option at config time.
39554      * @param {Boolean} value True to allow the user to directly edit the field text
39555      */
39556     setEditable : function(value){
39557         if(value == this.editable){
39558             return;
39559         }
39560         this.editable = value;
39561         if(!value){
39562             this.el.dom.setAttribute('readOnly', true);
39563             this.el.on('mousedown', this.onTriggerClick,  this);
39564             this.el.addClass('x-combo-noedit');
39565         }else{
39566             this.el.dom.setAttribute('readOnly', false);
39567             this.el.un('mousedown', this.onTriggerClick,  this);
39568             this.el.removeClass('x-combo-noedit');
39569         }
39570     },
39571
39572     // private
39573     onBeforeLoad : function(){
39574         if(!this.hasFocus){
39575             return;
39576         }
39577         this.innerList.update(this.loadingText ?
39578                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39579         this.restrictHeight();
39580         this.selectedIndex = -1;
39581     },
39582
39583     // private
39584     onLoad : function(){
39585         if(!this.hasFocus){
39586             return;
39587         }
39588         if(this.store.getCount() > 0){
39589             this.expand();
39590             this.restrictHeight();
39591             if(this.lastQuery == this.allQuery){
39592                 if(this.editable){
39593                     this.el.dom.select();
39594                 }
39595                 if(!this.selectByValue(this.value, true)){
39596                     this.select(0, true);
39597                 }
39598             }else{
39599                 this.selectNext();
39600                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39601                     this.taTask.delay(this.typeAheadDelay);
39602                 }
39603             }
39604         }else{
39605             this.onEmptyResults();
39606         }
39607         //this.el.focus();
39608     },
39609     // private
39610     onLoadException : function()
39611     {
39612         this.collapse();
39613         Roo.log(this.store.reader.jsonData);
39614         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39615             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39616         }
39617         
39618         
39619     },
39620     // private
39621     onTypeAhead : function(){
39622         if(this.store.getCount() > 0){
39623             var r = this.store.getAt(0);
39624             var newValue = r.data[this.displayField];
39625             var len = newValue.length;
39626             var selStart = this.getRawValue().length;
39627             if(selStart != len){
39628                 this.setRawValue(newValue);
39629                 this.selectText(selStart, newValue.length);
39630             }
39631         }
39632     },
39633
39634     // private
39635     onSelect : function(record, index){
39636         if(this.fireEvent('beforeselect', this, record, index) !== false){
39637             this.setFromData(index > -1 ? record.data : false);
39638             this.collapse();
39639             this.fireEvent('select', this, record, index);
39640         }
39641     },
39642
39643     /**
39644      * Returns the currently selected field value or empty string if no value is set.
39645      * @return {String} value The selected value
39646      */
39647     getValue : function(){
39648         if(this.valueField){
39649             return typeof this.value != 'undefined' ? this.value : '';
39650         }else{
39651             return Roo.form.ComboBox.superclass.getValue.call(this);
39652         }
39653     },
39654
39655     /**
39656      * Clears any text/value currently set in the field
39657      */
39658     clearValue : function(){
39659         if(this.hiddenField){
39660             this.hiddenField.value = '';
39661         }
39662         this.value = '';
39663         this.setRawValue('');
39664         this.lastSelectionText = '';
39665         
39666     },
39667
39668     /**
39669      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39670      * will be displayed in the field.  If the value does not match the data value of an existing item,
39671      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39672      * Otherwise the field will be blank (although the value will still be set).
39673      * @param {String} value The value to match
39674      */
39675     setValue : function(v){
39676         var text = v;
39677         if(this.valueField){
39678             var r = this.findRecord(this.valueField, v);
39679             if(r){
39680                 text = r.data[this.displayField];
39681             }else if(this.valueNotFoundText !== undefined){
39682                 text = this.valueNotFoundText;
39683             }
39684         }
39685         this.lastSelectionText = text;
39686         if(this.hiddenField){
39687             this.hiddenField.value = v;
39688         }
39689         Roo.form.ComboBox.superclass.setValue.call(this, text);
39690         this.value = v;
39691     },
39692     /**
39693      * @property {Object} the last set data for the element
39694      */
39695     
39696     lastData : false,
39697     /**
39698      * Sets the value of the field based on a object which is related to the record format for the store.
39699      * @param {Object} value the value to set as. or false on reset?
39700      */
39701     setFromData : function(o){
39702         var dv = ''; // display value
39703         var vv = ''; // value value..
39704         this.lastData = o;
39705         if (this.displayField) {
39706             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39707         } else {
39708             // this is an error condition!!!
39709             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39710         }
39711         
39712         if(this.valueField){
39713             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39714         }
39715         if(this.hiddenField){
39716             this.hiddenField.value = vv;
39717             
39718             this.lastSelectionText = dv;
39719             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39720             this.value = vv;
39721             return;
39722         }
39723         // no hidden field.. - we store the value in 'value', but still display
39724         // display field!!!!
39725         this.lastSelectionText = dv;
39726         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39727         this.value = vv;
39728         
39729         
39730     },
39731     // private
39732     reset : function(){
39733         // overridden so that last data is reset..
39734         this.setValue(this.resetValue);
39735         this.clearInvalid();
39736         this.lastData = false;
39737         if (this.view) {
39738             this.view.clearSelections();
39739         }
39740     },
39741     // private
39742     findRecord : function(prop, value){
39743         var record;
39744         if(this.store.getCount() > 0){
39745             this.store.each(function(r){
39746                 if(r.data[prop] == value){
39747                     record = r;
39748                     return false;
39749                 }
39750                 return true;
39751             });
39752         }
39753         return record;
39754     },
39755     
39756     getName: function()
39757     {
39758         // returns hidden if it's set..
39759         if (!this.rendered) {return ''};
39760         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39761         
39762     },
39763     // private
39764     onViewMove : function(e, t){
39765         this.inKeyMode = false;
39766     },
39767
39768     // private
39769     onViewOver : function(e, t){
39770         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39771             return;
39772         }
39773         var item = this.view.findItemFromChild(t);
39774         if(item){
39775             var index = this.view.indexOf(item);
39776             this.select(index, false);
39777         }
39778     },
39779
39780     // private
39781     onViewClick : function(doFocus)
39782     {
39783         var index = this.view.getSelectedIndexes()[0];
39784         var r = this.store.getAt(index);
39785         if(r){
39786             this.onSelect(r, index);
39787         }
39788         if(doFocus !== false && !this.blockFocus){
39789             this.el.focus();
39790         }
39791     },
39792
39793     // private
39794     restrictHeight : function(){
39795         this.innerList.dom.style.height = '';
39796         var inner = this.innerList.dom;
39797         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39798         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39799         this.list.beginUpdate();
39800         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39801         this.list.alignTo(this.el, this.listAlign);
39802         this.list.endUpdate();
39803     },
39804
39805     // private
39806     onEmptyResults : function(){
39807         this.collapse();
39808     },
39809
39810     /**
39811      * Returns true if the dropdown list is expanded, else false.
39812      */
39813     isExpanded : function(){
39814         return this.list.isVisible();
39815     },
39816
39817     /**
39818      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39819      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39820      * @param {String} value The data value of the item to select
39821      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39822      * selected item if it is not currently in view (defaults to true)
39823      * @return {Boolean} True if the value matched an item in the list, else false
39824      */
39825     selectByValue : function(v, scrollIntoView){
39826         if(v !== undefined && v !== null){
39827             var r = this.findRecord(this.valueField || this.displayField, v);
39828             if(r){
39829                 this.select(this.store.indexOf(r), scrollIntoView);
39830                 return true;
39831             }
39832         }
39833         return false;
39834     },
39835
39836     /**
39837      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39838      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39839      * @param {Number} index The zero-based index of the list item to select
39840      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39841      * selected item if it is not currently in view (defaults to true)
39842      */
39843     select : function(index, scrollIntoView){
39844         this.selectedIndex = index;
39845         this.view.select(index);
39846         if(scrollIntoView !== false){
39847             var el = this.view.getNode(index);
39848             if(el){
39849                 this.innerList.scrollChildIntoView(el, false);
39850             }
39851         }
39852     },
39853
39854     // private
39855     selectNext : function(){
39856         var ct = this.store.getCount();
39857         if(ct > 0){
39858             if(this.selectedIndex == -1){
39859                 this.select(0);
39860             }else if(this.selectedIndex < ct-1){
39861                 this.select(this.selectedIndex+1);
39862             }
39863         }
39864     },
39865
39866     // private
39867     selectPrev : function(){
39868         var ct = this.store.getCount();
39869         if(ct > 0){
39870             if(this.selectedIndex == -1){
39871                 this.select(0);
39872             }else if(this.selectedIndex != 0){
39873                 this.select(this.selectedIndex-1);
39874             }
39875         }
39876     },
39877
39878     // private
39879     onKeyUp : function(e){
39880         if(this.editable !== false && !e.isSpecialKey()){
39881             this.lastKey = e.getKey();
39882             this.dqTask.delay(this.queryDelay);
39883         }
39884     },
39885
39886     // private
39887     validateBlur : function(){
39888         return !this.list || !this.list.isVisible();   
39889     },
39890
39891     // private
39892     initQuery : function(){
39893         this.doQuery(this.getRawValue());
39894     },
39895
39896     // private
39897     doForce : function(){
39898         if(this.el.dom.value.length > 0){
39899             this.el.dom.value =
39900                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39901              
39902         }
39903     },
39904
39905     /**
39906      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39907      * query allowing the query action to be canceled if needed.
39908      * @param {String} query The SQL query to execute
39909      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39910      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39911      * saved in the current store (defaults to false)
39912      */
39913     doQuery : function(q, forceAll){
39914         if(q === undefined || q === null){
39915             q = '';
39916         }
39917         var qe = {
39918             query: q,
39919             forceAll: forceAll,
39920             combo: this,
39921             cancel:false
39922         };
39923         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39924             return false;
39925         }
39926         q = qe.query;
39927         forceAll = qe.forceAll;
39928         if(forceAll === true || (q.length >= this.minChars)){
39929             if(this.lastQuery != q || this.alwaysQuery){
39930                 this.lastQuery = q;
39931                 if(this.mode == 'local'){
39932                     this.selectedIndex = -1;
39933                     if(forceAll){
39934                         this.store.clearFilter();
39935                     }else{
39936                         this.store.filter(this.displayField, q);
39937                     }
39938                     this.onLoad();
39939                 }else{
39940                     this.store.baseParams[this.queryParam] = q;
39941                     this.store.load({
39942                         params: this.getParams(q)
39943                     });
39944                     this.expand();
39945                 }
39946             }else{
39947                 this.selectedIndex = -1;
39948                 this.onLoad();   
39949             }
39950         }
39951     },
39952
39953     // private
39954     getParams : function(q){
39955         var p = {};
39956         //p[this.queryParam] = q;
39957         if(this.pageSize){
39958             p.start = 0;
39959             p.limit = this.pageSize;
39960         }
39961         return p;
39962     },
39963
39964     /**
39965      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39966      */
39967     collapse : function(){
39968         if(!this.isExpanded()){
39969             return;
39970         }
39971         this.list.hide();
39972         Roo.get(document).un('mousedown', this.collapseIf, this);
39973         Roo.get(document).un('mousewheel', this.collapseIf, this);
39974         if (!this.editable) {
39975             Roo.get(document).un('keydown', this.listKeyPress, this);
39976         }
39977         this.fireEvent('collapse', this);
39978     },
39979
39980     // private
39981     collapseIf : function(e){
39982         if(!e.within(this.wrap) && !e.within(this.list)){
39983             this.collapse();
39984         }
39985     },
39986
39987     /**
39988      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39989      */
39990     expand : function(){
39991         if(this.isExpanded() || !this.hasFocus){
39992             return;
39993         }
39994         this.list.alignTo(this.el, this.listAlign);
39995         this.list.show();
39996         Roo.get(document).on('mousedown', this.collapseIf, this);
39997         Roo.get(document).on('mousewheel', this.collapseIf, this);
39998         if (!this.editable) {
39999             Roo.get(document).on('keydown', this.listKeyPress, this);
40000         }
40001         
40002         this.fireEvent('expand', this);
40003     },
40004
40005     // private
40006     // Implements the default empty TriggerField.onTriggerClick function
40007     onTriggerClick : function(){
40008         if(this.disabled){
40009             return;
40010         }
40011         if(this.isExpanded()){
40012             this.collapse();
40013             if (!this.blockFocus) {
40014                 this.el.focus();
40015             }
40016             
40017         }else {
40018             this.hasFocus = true;
40019             if(this.triggerAction == 'all') {
40020                 this.doQuery(this.allQuery, true);
40021             } else {
40022                 this.doQuery(this.getRawValue());
40023             }
40024             if (!this.blockFocus) {
40025                 this.el.focus();
40026             }
40027         }
40028     },
40029     listKeyPress : function(e)
40030     {
40031         //Roo.log('listkeypress');
40032         // scroll to first matching element based on key pres..
40033         if (e.isSpecialKey()) {
40034             return false;
40035         }
40036         var k = String.fromCharCode(e.getKey()).toUpperCase();
40037         //Roo.log(k);
40038         var match  = false;
40039         var csel = this.view.getSelectedNodes();
40040         var cselitem = false;
40041         if (csel.length) {
40042             var ix = this.view.indexOf(csel[0]);
40043             cselitem  = this.store.getAt(ix);
40044             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40045                 cselitem = false;
40046             }
40047             
40048         }
40049         
40050         this.store.each(function(v) { 
40051             if (cselitem) {
40052                 // start at existing selection.
40053                 if (cselitem.id == v.id) {
40054                     cselitem = false;
40055                 }
40056                 return;
40057             }
40058                 
40059             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40060                 match = this.store.indexOf(v);
40061                 return false;
40062             }
40063         }, this);
40064         
40065         if (match === false) {
40066             return true; // no more action?
40067         }
40068         // scroll to?
40069         this.view.select(match);
40070         var sn = Roo.get(this.view.getSelectedNodes()[0])
40071         sn.scrollIntoView(sn.dom.parentNode, false);
40072     }
40073
40074     /** 
40075     * @cfg {Boolean} grow 
40076     * @hide 
40077     */
40078     /** 
40079     * @cfg {Number} growMin 
40080     * @hide 
40081     */
40082     /** 
40083     * @cfg {Number} growMax 
40084     * @hide 
40085     */
40086     /**
40087      * @hide
40088      * @method autoSize
40089      */
40090 });/*
40091  * Copyright(c) 2010-2012, Roo J Solutions Limited
40092  *
40093  * Licence LGPL
40094  *
40095  */
40096
40097 /**
40098  * @class Roo.form.ComboBoxArray
40099  * @extends Roo.form.TextField
40100  * A facebook style adder... for lists of email / people / countries  etc...
40101  * pick multiple items from a combo box, and shows each one.
40102  *
40103  *  Fred [x]  Brian [x]  [Pick another |v]
40104  *
40105  *
40106  *  For this to work: it needs various extra information
40107  *    - normal combo problay has
40108  *      name, hiddenName
40109  *    + displayField, valueField
40110  *
40111  *    For our purpose...
40112  *
40113  *
40114  *   If we change from 'extends' to wrapping...
40115  *   
40116  *  
40117  *
40118  
40119  
40120  * @constructor
40121  * Create a new ComboBoxArray.
40122  * @param {Object} config Configuration options
40123  */
40124  
40125
40126 Roo.form.ComboBoxArray = function(config)
40127 {
40128     
40129     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40130     
40131     this.items = new Roo.util.MixedCollection(false);
40132     
40133     // construct the child combo...
40134     
40135     
40136     
40137     
40138    
40139     
40140 }
40141
40142  
40143 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40144
40145     /**
40146      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40147      */
40148     
40149     lastData : false,
40150     
40151     // behavies liek a hiddne field
40152     inputType:      'hidden',
40153     /**
40154      * @cfg {Number} width The width of the box that displays the selected element
40155      */ 
40156     width:          300,
40157
40158     
40159     
40160     /**
40161      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40162      */
40163     name : false,
40164     /**
40165      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40166      */
40167     hiddenName : false,
40168     
40169     
40170     // private the array of items that are displayed..
40171     items  : false,
40172     // private - the hidden field el.
40173     hiddenEl : false,
40174     // private - the filed el..
40175     el : false,
40176     
40177     //validateValue : function() { return true; }, // all values are ok!
40178     //onAddClick: function() { },
40179     
40180     onRender : function(ct, position) 
40181     {
40182         
40183         // create the standard hidden element
40184         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40185         
40186         
40187         // give fake names to child combo;
40188         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40189         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40190         
40191         this.combo = Roo.factory(this.combo, Roo.form);
40192         this.combo.onRender(ct, position);
40193         if (typeof(this.combo.width) != 'undefined') {
40194             this.combo.onResize(this.combo.width,0);
40195         }
40196         
40197         this.combo.initEvents();
40198         
40199         // assigned so form know we need to do this..
40200         this.store          = this.combo.store;
40201         this.valueField     = this.combo.valueField;
40202         this.displayField   = this.combo.displayField ;
40203         
40204         
40205         this.combo.wrap.addClass('x-cbarray-grp');
40206         
40207         var cbwrap = this.combo.wrap.createChild(
40208             {tag: 'div', cls: 'x-cbarray-cb'},
40209             this.combo.el.dom
40210         );
40211         
40212              
40213         this.hiddenEl = this.combo.wrap.createChild({
40214             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40215         });
40216         this.el = this.combo.wrap.createChild({
40217             tag: 'input',  type:'hidden' , name: this.name, value : ''
40218         });
40219          //   this.el.dom.removeAttribute("name");
40220         
40221         
40222         this.outerWrap = this.combo.wrap;
40223         this.wrap = cbwrap;
40224         
40225         this.outerWrap.setWidth(this.width);
40226         this.outerWrap.dom.removeChild(this.el.dom);
40227         
40228         this.wrap.dom.appendChild(this.el.dom);
40229         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40230         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40231         
40232         this.combo.trigger.setStyle('position','relative');
40233         this.combo.trigger.setStyle('left', '0px');
40234         this.combo.trigger.setStyle('top', '2px');
40235         
40236         this.combo.el.setStyle('vertical-align', 'text-bottom');
40237         
40238         //this.trigger.setStyle('vertical-align', 'top');
40239         
40240         // this should use the code from combo really... on('add' ....)
40241         if (this.adder) {
40242             
40243         
40244             this.adder = this.outerWrap.createChild(
40245                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40246             var _t = this;
40247             this.adder.on('click', function(e) {
40248                 _t.fireEvent('adderclick', this, e);
40249             }, _t);
40250         }
40251         //var _t = this;
40252         //this.adder.on('click', this.onAddClick, _t);
40253         
40254         
40255         this.combo.on('select', function(cb, rec, ix) {
40256             this.addItem(rec.data);
40257             
40258             cb.setValue('');
40259             cb.el.dom.value = '';
40260             //cb.lastData = rec.data;
40261             // add to list
40262             
40263         }, this);
40264         
40265         
40266     },
40267     
40268     
40269     getName: function()
40270     {
40271         // returns hidden if it's set..
40272         if (!this.rendered) {return ''};
40273         return  this.hiddenName ? this.hiddenName : this.name;
40274         
40275     },
40276     
40277     
40278     onResize: function(w, h){
40279         
40280         return;
40281         // not sure if this is needed..
40282         //this.combo.onResize(w,h);
40283         
40284         if(typeof w != 'number'){
40285             // we do not handle it!?!?
40286             return;
40287         }
40288         var tw = this.combo.trigger.getWidth();
40289         tw += this.addicon ? this.addicon.getWidth() : 0;
40290         tw += this.editicon ? this.editicon.getWidth() : 0;
40291         var x = w - tw;
40292         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40293             
40294         this.combo.trigger.setStyle('left', '0px');
40295         
40296         if(this.list && this.listWidth === undefined){
40297             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40298             this.list.setWidth(lw);
40299             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40300         }
40301         
40302     
40303         
40304     },
40305     
40306     addItem: function(rec)
40307     {
40308         var valueField = this.combo.valueField;
40309         var displayField = this.combo.displayField;
40310         if (this.items.indexOfKey(rec[valueField]) > -1) {
40311             //console.log("GOT " + rec.data.id);
40312             return;
40313         }
40314         
40315         var x = new Roo.form.ComboBoxArray.Item({
40316             //id : rec[this.idField],
40317             data : rec,
40318             displayField : displayField ,
40319             tipField : displayField ,
40320             cb : this
40321         });
40322         // use the 
40323         this.items.add(rec[valueField],x);
40324         // add it before the element..
40325         this.updateHiddenEl();
40326         x.render(this.outerWrap, this.wrap.dom);
40327         // add the image handler..
40328     },
40329     
40330     updateHiddenEl : function()
40331     {
40332         this.validate();
40333         if (!this.hiddenEl) {
40334             return;
40335         }
40336         var ar = [];
40337         var idField = this.combo.valueField;
40338         
40339         this.items.each(function(f) {
40340             ar.push(f.data[idField]);
40341            
40342         });
40343         this.hiddenEl.dom.value = ar.join(',');
40344         this.validate();
40345     },
40346     
40347     reset : function()
40348     {
40349         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40350         this.items.each(function(f) {
40351            f.remove(); 
40352         });
40353         this.el.dom.value = '';
40354         if (this.hiddenEl) {
40355             this.hiddenEl.dom.value = '';
40356         }
40357         
40358     },
40359     getValue: function()
40360     {
40361         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40362     },
40363     setValue: function(v) // not a valid action - must use addItems..
40364     {
40365          
40366         this.reset();
40367         
40368         
40369         
40370         if (this.store.isLocal && (typeof(v) == 'string')) {
40371             // then we can use the store to find the values..
40372             // comma seperated at present.. this needs to allow JSON based encoding..
40373             this.hiddenEl.value  = v;
40374             var v_ar = [];
40375             Roo.each(v.split(','), function(k) {
40376                 Roo.log("CHECK " + this.valueField + ',' + k);
40377                 var li = this.store.query(this.valueField, k);
40378                 if (!li.length) {
40379                     return;
40380                 }
40381                 var add = {};
40382                 add[this.valueField] = k;
40383                 add[this.displayField] = li.item(0).data[this.displayField];
40384                 
40385                 this.addItem(add);
40386             }, this) 
40387              
40388         }
40389         if (typeof(v) == 'object') {
40390             // then let's assume it's an array of objects..
40391             Roo.each(v, function(l) {
40392                 this.addItem(l);
40393             }, this);
40394              
40395         }
40396         
40397         
40398     },
40399     setFromData: function(v)
40400     {
40401         // this recieves an object, if setValues is called.
40402         this.reset();
40403         this.el.dom.value = v[this.displayField];
40404         this.hiddenEl.dom.value = v[this.valueField];
40405         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40406             return;
40407         }
40408         var kv = v[this.valueField];
40409         var dv = v[this.displayField];
40410         kv = typeof(kv) != 'string' ? '' : kv;
40411         dv = typeof(dv) != 'string' ? '' : dv;
40412         
40413         
40414         var keys = kv.split(',');
40415         var display = dv.split(',');
40416         for (var i = 0 ; i < keys.length; i++) {
40417             
40418             add = {};
40419             add[this.valueField] = keys[i];
40420             add[this.displayField] = display[i];
40421             this.addItem(add);
40422         }
40423       
40424         
40425     },
40426     
40427     /**
40428      * Validates the combox array value
40429      * @return {Boolean} True if the value is valid, else false
40430      */
40431     validate : function(){
40432         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40433             this.clearInvalid();
40434             return true;
40435         }
40436         return false;
40437     },
40438     
40439     validateValue : function(value){
40440         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40441         
40442     },
40443     
40444     /*@
40445      * overide
40446      * 
40447      */
40448     isDirty : function() {
40449         if(this.disabled) {
40450             return false;
40451         }
40452         
40453         try {
40454             var d = Roo.decode(String(this.originalValue));
40455         } catch (e) {
40456             return String(this.getValue()) !== String(this.originalValue);
40457         }
40458         
40459         var originalValue = [];
40460         
40461         for (var i = 0; i < d.length; i++){
40462             originalValue.push(d[i][this.valueField]);
40463         }
40464         
40465         return String(this.getValue()) !== String(originalValue.join(','));
40466         
40467     }
40468     
40469 });
40470
40471
40472
40473 /**
40474  * @class Roo.form.ComboBoxArray.Item
40475  * @extends Roo.BoxComponent
40476  * A selected item in the list
40477  *  Fred [x]  Brian [x]  [Pick another |v]
40478  * 
40479  * @constructor
40480  * Create a new item.
40481  * @param {Object} config Configuration options
40482  */
40483  
40484 Roo.form.ComboBoxArray.Item = function(config) {
40485     config.id = Roo.id();
40486     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40487 }
40488
40489 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40490     data : {},
40491     cb: false,
40492     displayField : false,
40493     tipField : false,
40494     
40495     
40496     defaultAutoCreate : {
40497         tag: 'div',
40498         cls: 'x-cbarray-item',
40499         cn : [ 
40500             { tag: 'div' },
40501             {
40502                 tag: 'img',
40503                 width:16,
40504                 height : 16,
40505                 src : Roo.BLANK_IMAGE_URL ,
40506                 align: 'center'
40507             }
40508         ]
40509         
40510     },
40511     
40512  
40513     onRender : function(ct, position)
40514     {
40515         Roo.form.Field.superclass.onRender.call(this, ct, position);
40516         
40517         if(!this.el){
40518             var cfg = this.getAutoCreate();
40519             this.el = ct.createChild(cfg, position);
40520         }
40521         
40522         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40523         
40524         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40525             this.cb.renderer(this.data) :
40526             String.format('{0}',this.data[this.displayField]);
40527         
40528             
40529         this.el.child('div').dom.setAttribute('qtip',
40530                         String.format('{0}',this.data[this.tipField])
40531         );
40532         
40533         this.el.child('img').on('click', this.remove, this);
40534         
40535     },
40536    
40537     remove : function()
40538     {
40539         
40540         this.cb.items.remove(this);
40541         this.el.child('img').un('click', this.remove, this);
40542         this.el.remove();
40543         this.cb.updateHiddenEl();
40544     }
40545 });/*
40546  * Based on:
40547  * Ext JS Library 1.1.1
40548  * Copyright(c) 2006-2007, Ext JS, LLC.
40549  *
40550  * Originally Released Under LGPL - original licence link has changed is not relivant.
40551  *
40552  * Fork - LGPL
40553  * <script type="text/javascript">
40554  */
40555 /**
40556  * @class Roo.form.Checkbox
40557  * @extends Roo.form.Field
40558  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40559  * @constructor
40560  * Creates a new Checkbox
40561  * @param {Object} config Configuration options
40562  */
40563 Roo.form.Checkbox = function(config){
40564     Roo.form.Checkbox.superclass.constructor.call(this, config);
40565     this.addEvents({
40566         /**
40567          * @event check
40568          * Fires when the checkbox is checked or unchecked.
40569              * @param {Roo.form.Checkbox} this This checkbox
40570              * @param {Boolean} checked The new checked value
40571              */
40572         check : true
40573     });
40574 };
40575
40576 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40577     /**
40578      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40579      */
40580     focusClass : undefined,
40581     /**
40582      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40583      */
40584     fieldClass: "x-form-field",
40585     /**
40586      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40587      */
40588     checked: false,
40589     /**
40590      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40591      * {tag: "input", type: "checkbox", autocomplete: "off"})
40592      */
40593     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40594     /**
40595      * @cfg {String} boxLabel The text that appears beside the checkbox
40596      */
40597     boxLabel : "",
40598     /**
40599      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40600      */  
40601     inputValue : '1',
40602     /**
40603      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40604      */
40605      valueOff: '0', // value when not checked..
40606
40607     actionMode : 'viewEl', 
40608     //
40609     // private
40610     itemCls : 'x-menu-check-item x-form-item',
40611     groupClass : 'x-menu-group-item',
40612     inputType : 'hidden',
40613     
40614     
40615     inSetChecked: false, // check that we are not calling self...
40616     
40617     inputElement: false, // real input element?
40618     basedOn: false, // ????
40619     
40620     isFormField: true, // not sure where this is needed!!!!
40621
40622     onResize : function(){
40623         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40624         if(!this.boxLabel){
40625             this.el.alignTo(this.wrap, 'c-c');
40626         }
40627     },
40628
40629     initEvents : function(){
40630         Roo.form.Checkbox.superclass.initEvents.call(this);
40631         this.el.on("click", this.onClick,  this);
40632         this.el.on("change", this.onClick,  this);
40633     },
40634
40635
40636     getResizeEl : function(){
40637         return this.wrap;
40638     },
40639
40640     getPositionEl : function(){
40641         return this.wrap;
40642     },
40643
40644     // private
40645     onRender : function(ct, position){
40646         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40647         /*
40648         if(this.inputValue !== undefined){
40649             this.el.dom.value = this.inputValue;
40650         }
40651         */
40652         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40653         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40654         var viewEl = this.wrap.createChild({ 
40655             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40656         this.viewEl = viewEl;   
40657         this.wrap.on('click', this.onClick,  this); 
40658         
40659         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40660         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40661         
40662         
40663         
40664         if(this.boxLabel){
40665             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40666         //    viewEl.on('click', this.onClick,  this); 
40667         }
40668         //if(this.checked){
40669             this.setChecked(this.checked);
40670         //}else{
40671             //this.checked = this.el.dom;
40672         //}
40673
40674     },
40675
40676     // private
40677     initValue : Roo.emptyFn,
40678
40679     /**
40680      * Returns the checked state of the checkbox.
40681      * @return {Boolean} True if checked, else false
40682      */
40683     getValue : function(){
40684         if(this.el){
40685             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40686         }
40687         return this.valueOff;
40688         
40689     },
40690
40691         // private
40692     onClick : function(){ 
40693         this.setChecked(!this.checked);
40694
40695         //if(this.el.dom.checked != this.checked){
40696         //    this.setValue(this.el.dom.checked);
40697        // }
40698     },
40699
40700     /**
40701      * Sets the checked state of the checkbox.
40702      * On is always based on a string comparison between inputValue and the param.
40703      * @param {Boolean/String} value - the value to set 
40704      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40705      */
40706     setValue : function(v,suppressEvent){
40707         
40708         
40709         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40710         //if(this.el && this.el.dom){
40711         //    this.el.dom.checked = this.checked;
40712         //    this.el.dom.defaultChecked = this.checked;
40713         //}
40714         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40715         //this.fireEvent("check", this, this.checked);
40716     },
40717     // private..
40718     setChecked : function(state,suppressEvent)
40719     {
40720         if (this.inSetChecked) {
40721             this.checked = state;
40722             return;
40723         }
40724         
40725     
40726         if(this.wrap){
40727             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40728         }
40729         this.checked = state;
40730         if(suppressEvent !== true){
40731             this.fireEvent('check', this, state);
40732         }
40733         this.inSetChecked = true;
40734         this.el.dom.value = state ? this.inputValue : this.valueOff;
40735         this.inSetChecked = false;
40736         
40737     },
40738     // handle setting of hidden value by some other method!!?!?
40739     setFromHidden: function()
40740     {
40741         if(!this.el){
40742             return;
40743         }
40744         //console.log("SET FROM HIDDEN");
40745         //alert('setFrom hidden');
40746         this.setValue(this.el.dom.value);
40747     },
40748     
40749     onDestroy : function()
40750     {
40751         if(this.viewEl){
40752             Roo.get(this.viewEl).remove();
40753         }
40754          
40755         Roo.form.Checkbox.superclass.onDestroy.call(this);
40756     }
40757
40758 });/*
40759  * Based on:
40760  * Ext JS Library 1.1.1
40761  * Copyright(c) 2006-2007, Ext JS, LLC.
40762  *
40763  * Originally Released Under LGPL - original licence link has changed is not relivant.
40764  *
40765  * Fork - LGPL
40766  * <script type="text/javascript">
40767  */
40768  
40769 /**
40770  * @class Roo.form.Radio
40771  * @extends Roo.form.Checkbox
40772  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40773  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40774  * @constructor
40775  * Creates a new Radio
40776  * @param {Object} config Configuration options
40777  */
40778 Roo.form.Radio = function(){
40779     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40780 };
40781 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40782     inputType: 'radio',
40783
40784     /**
40785      * If this radio is part of a group, it will return the selected value
40786      * @return {String}
40787      */
40788     getGroupValue : function(){
40789         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40790     },
40791     
40792     
40793     onRender : function(ct, position){
40794         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40795         
40796         if(this.inputValue !== undefined){
40797             this.el.dom.value = this.inputValue;
40798         }
40799          
40800         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40801         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40802         //var viewEl = this.wrap.createChild({ 
40803         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40804         //this.viewEl = viewEl;   
40805         //this.wrap.on('click', this.onClick,  this); 
40806         
40807         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40808         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40809         
40810         
40811         
40812         if(this.boxLabel){
40813             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40814         //    viewEl.on('click', this.onClick,  this); 
40815         }
40816          if(this.checked){
40817             this.el.dom.checked =   'checked' ;
40818         }
40819          
40820     } 
40821     
40822     
40823 });//<script type="text/javascript">
40824
40825 /*
40826  * Ext JS Library 1.1.1
40827  * Copyright(c) 2006-2007, Ext JS, LLC.
40828  * licensing@extjs.com
40829  * 
40830  * http://www.extjs.com/license
40831  */
40832  
40833  /*
40834   * 
40835   * Known bugs:
40836   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40837   * - IE ? - no idea how much works there.
40838   * 
40839   * 
40840   * 
40841   */
40842  
40843
40844 /**
40845  * @class Ext.form.HtmlEditor
40846  * @extends Ext.form.Field
40847  * Provides a lightweight HTML Editor component.
40848  *
40849  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40850  * 
40851  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40852  * supported by this editor.</b><br/><br/>
40853  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40854  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40855  */
40856 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40857       /**
40858      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40859      */
40860     toolbars : false,
40861     /**
40862      * @cfg {String} createLinkText The default text for the create link prompt
40863      */
40864     createLinkText : 'Please enter the URL for the link:',
40865     /**
40866      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40867      */
40868     defaultLinkValue : 'http:/'+'/',
40869    
40870      /**
40871      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40872      *                        Roo.resizable.
40873      */
40874     resizable : false,
40875      /**
40876      * @cfg {Number} height (in pixels)
40877      */   
40878     height: 300,
40879    /**
40880      * @cfg {Number} width (in pixels)
40881      */   
40882     width: 500,
40883     
40884     /**
40885      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40886      * 
40887      */
40888     stylesheets: false,
40889     
40890     // id of frame..
40891     frameId: false,
40892     
40893     // private properties
40894     validationEvent : false,
40895     deferHeight: true,
40896     initialized : false,
40897     activated : false,
40898     sourceEditMode : false,
40899     onFocus : Roo.emptyFn,
40900     iframePad:3,
40901     hideMode:'offsets',
40902     
40903     defaultAutoCreate : { // modified by initCompnoent..
40904         tag: "textarea",
40905         style:"width:500px;height:300px;",
40906         autocomplete: "off"
40907     },
40908
40909     // private
40910     initComponent : function(){
40911         this.addEvents({
40912             /**
40913              * @event initialize
40914              * Fires when the editor is fully initialized (including the iframe)
40915              * @param {HtmlEditor} this
40916              */
40917             initialize: true,
40918             /**
40919              * @event activate
40920              * Fires when the editor is first receives the focus. Any insertion must wait
40921              * until after this event.
40922              * @param {HtmlEditor} this
40923              */
40924             activate: true,
40925              /**
40926              * @event beforesync
40927              * Fires before the textarea is updated with content from the editor iframe. Return false
40928              * to cancel the sync.
40929              * @param {HtmlEditor} this
40930              * @param {String} html
40931              */
40932             beforesync: true,
40933              /**
40934              * @event beforepush
40935              * Fires before the iframe editor is updated with content from the textarea. Return false
40936              * to cancel the push.
40937              * @param {HtmlEditor} this
40938              * @param {String} html
40939              */
40940             beforepush: true,
40941              /**
40942              * @event sync
40943              * Fires when the textarea is updated with content from the editor iframe.
40944              * @param {HtmlEditor} this
40945              * @param {String} html
40946              */
40947             sync: true,
40948              /**
40949              * @event push
40950              * Fires when the iframe editor is updated with content from the textarea.
40951              * @param {HtmlEditor} this
40952              * @param {String} html
40953              */
40954             push: true,
40955              /**
40956              * @event editmodechange
40957              * Fires when the editor switches edit modes
40958              * @param {HtmlEditor} this
40959              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40960              */
40961             editmodechange: true,
40962             /**
40963              * @event editorevent
40964              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40965              * @param {HtmlEditor} this
40966              */
40967             editorevent: true
40968         });
40969         this.defaultAutoCreate =  {
40970             tag: "textarea",
40971             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40972             autocomplete: "off"
40973         };
40974     },
40975
40976     /**
40977      * Protected method that will not generally be called directly. It
40978      * is called when the editor creates its toolbar. Override this method if you need to
40979      * add custom toolbar buttons.
40980      * @param {HtmlEditor} editor
40981      */
40982     createToolbar : function(editor){
40983         if (!editor.toolbars || !editor.toolbars.length) {
40984             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40985         }
40986         
40987         for (var i =0 ; i < editor.toolbars.length;i++) {
40988             editor.toolbars[i] = Roo.factory(
40989                     typeof(editor.toolbars[i]) == 'string' ?
40990                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40991                 Roo.form.HtmlEditor);
40992             editor.toolbars[i].init(editor);
40993         }
40994          
40995         
40996     },
40997
40998     /**
40999      * Protected method that will not generally be called directly. It
41000      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41001      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41002      */
41003     getDocMarkup : function(){
41004         // body styles..
41005         var st = '';
41006         if (this.stylesheets === false) {
41007             
41008             Roo.get(document.head).select('style').each(function(node) {
41009                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41010             });
41011             
41012             Roo.get(document.head).select('link').each(function(node) { 
41013                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41014             });
41015             
41016         } else if (!this.stylesheets.length) {
41017                 // simple..
41018                 st = '<style type="text/css">' +
41019                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41020                    '</style>';
41021         } else {
41022             Roo.log('this.stylesheets');
41023             Roo.log(this.stylesheet);
41024             Roo.each(this.stylesheets, function(s) {
41025                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41026             });
41027             
41028         }
41029         
41030         st +=  '<style type="text/css">' +
41031             'IMG { cursor: pointer } ' +
41032         '</style>';
41033
41034         
41035         return '<html><head>' + st  +
41036             //<style type="text/css">' +
41037             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41038             //'</style>' +
41039             ' </head><body class="roo-htmleditor-body"></body></html>';
41040     },
41041
41042     // private
41043     onRender : function(ct, position)
41044     {
41045         var _t = this;
41046         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
41047         this.el.dom.style.border = '0 none';
41048         this.el.dom.setAttribute('tabIndex', -1);
41049         this.el.addClass('x-hidden');
41050         if(Roo.isIE){ // fix IE 1px bogus margin
41051             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41052         }
41053         this.wrap = this.el.wrap({
41054             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
41055         });
41056         
41057         if (this.resizable) {
41058             this.resizeEl = new Roo.Resizable(this.wrap, {
41059                 pinned : true,
41060                 wrap: true,
41061                 dynamic : true,
41062                 minHeight : this.height,
41063                 height: this.height,
41064                 handles : this.resizable,
41065                 width: this.width,
41066                 listeners : {
41067                     resize : function(r, w, h) {
41068                         _t.onResize(w,h); // -something
41069                     }
41070                 }
41071             });
41072             
41073         }
41074
41075         this.frameId = Roo.id();
41076         
41077         this.createToolbar(this);
41078         
41079       
41080         
41081         var iframe = this.wrap.createChild({
41082             tag: 'iframe',
41083             id: this.frameId,
41084             name: this.frameId,
41085             frameBorder : 'no',
41086             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41087         }, this.el
41088         );
41089         
41090        // console.log(iframe);
41091         //this.wrap.dom.appendChild(iframe);
41092
41093         this.iframe = iframe.dom;
41094
41095          this.assignDocWin();
41096         
41097         this.doc.designMode = 'on';
41098        
41099         this.doc.open();
41100         this.doc.write(this.getDocMarkup());
41101         this.doc.close();
41102
41103         
41104         var task = { // must defer to wait for browser to be ready
41105             run : function(){
41106                 //console.log("run task?" + this.doc.readyState);
41107                 this.assignDocWin();
41108                 if(this.doc.body || this.doc.readyState == 'complete'){
41109                     try {
41110                         this.doc.designMode="on";
41111                     } catch (e) {
41112                         return;
41113                     }
41114                     Roo.TaskMgr.stop(task);
41115                     this.initEditor.defer(10, this);
41116                 }
41117             },
41118             interval : 10,
41119             duration:10000,
41120             scope: this
41121         };
41122         Roo.TaskMgr.start(task);
41123
41124         if(!this.width){
41125             this.setSize(this.wrap.getSize());
41126         }
41127         if (this.resizeEl) {
41128             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
41129             // should trigger onReize..
41130         }
41131     },
41132
41133     // private
41134     onResize : function(w, h)
41135     {
41136         //Roo.log('resize: ' +w + ',' + h );
41137         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
41138         if(this.el && this.iframe){
41139             if(typeof w == 'number'){
41140                 var aw = w - this.wrap.getFrameWidth('lr');
41141                 this.el.setWidth(this.adjustWidth('textarea', aw));
41142                 this.iframe.style.width = aw + 'px';
41143             }
41144             if(typeof h == 'number'){
41145                 var tbh = 0;
41146                 for (var i =0; i < this.toolbars.length;i++) {
41147                     // fixme - ask toolbars for heights?
41148                     tbh += this.toolbars[i].tb.el.getHeight();
41149                     if (this.toolbars[i].footer) {
41150                         tbh += this.toolbars[i].footer.el.getHeight();
41151                     }
41152                 }
41153                 
41154                 
41155                 
41156                 
41157                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
41158                 ah -= 5; // knock a few pixes off for look..
41159                 this.el.setHeight(this.adjustWidth('textarea', ah));
41160                 this.iframe.style.height = ah + 'px';
41161                 if(this.doc){
41162                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
41163                 }
41164             }
41165         }
41166     },
41167
41168     /**
41169      * Toggles the editor between standard and source edit mode.
41170      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41171      */
41172     toggleSourceEdit : function(sourceEditMode){
41173         
41174         this.sourceEditMode = sourceEditMode === true;
41175         
41176         if(this.sourceEditMode){
41177 //            Roo.log('in');
41178 //            Roo.log(this.syncValue());
41179             this.syncValue();
41180             this.iframe.className = 'x-hidden';
41181             this.el.removeClass('x-hidden');
41182             this.el.dom.removeAttribute('tabIndex');
41183             this.el.focus();
41184         }else{
41185 //            Roo.log('out')
41186 //            Roo.log(this.pushValue()); 
41187             this.pushValue();
41188             this.iframe.className = '';
41189             this.el.addClass('x-hidden');
41190             this.el.dom.setAttribute('tabIndex', -1);
41191             this.deferFocus();
41192         }
41193         this.setSize(this.wrap.getSize());
41194         this.fireEvent('editmodechange', this, this.sourceEditMode);
41195     },
41196
41197     // private used internally
41198     createLink : function(){
41199         var url = prompt(this.createLinkText, this.defaultLinkValue);
41200         if(url && url != 'http:/'+'/'){
41201             this.relayCmd('createlink', url);
41202         }
41203     },
41204
41205     // private (for BoxComponent)
41206     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41207
41208     // private (for BoxComponent)
41209     getResizeEl : function(){
41210         return this.wrap;
41211     },
41212
41213     // private (for BoxComponent)
41214     getPositionEl : function(){
41215         return this.wrap;
41216     },
41217
41218     // private
41219     initEvents : function(){
41220         this.originalValue = this.getValue();
41221     },
41222
41223     /**
41224      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41225      * @method
41226      */
41227     markInvalid : Roo.emptyFn,
41228     /**
41229      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41230      * @method
41231      */
41232     clearInvalid : Roo.emptyFn,
41233
41234     setValue : function(v){
41235         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
41236         this.pushValue();
41237     },
41238
41239     /**
41240      * Protected method that will not generally be called directly. If you need/want
41241      * custom HTML cleanup, this is the method you should override.
41242      * @param {String} html The HTML to be cleaned
41243      * return {String} The cleaned HTML
41244      */
41245     cleanHtml : function(html){
41246         html = String(html);
41247         if(html.length > 5){
41248             if(Roo.isSafari){ // strip safari nonsense
41249                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41250             }
41251         }
41252         if(html == '&nbsp;'){
41253             html = '';
41254         }
41255         return html;
41256     },
41257
41258     /**
41259      * Protected method that will not generally be called directly. Syncs the contents
41260      * of the editor iframe with the textarea.
41261      */
41262     syncValue : function(){
41263         if(this.initialized){
41264             var bd = (this.doc.body || this.doc.documentElement);
41265             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41266             var html = bd.innerHTML;
41267             if(Roo.isSafari){
41268                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41269                 var m = bs.match(/text-align:(.*?);/i);
41270                 if(m && m[1]){
41271                     html = '<div style="'+m[0]+'">' + html + '</div>';
41272                 }
41273             }
41274             html = this.cleanHtml(html);
41275             // fix up the special chars.. normaly like back quotes in word...
41276             // however we do not want to do this with chinese..
41277             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41278                 var cc = b.charCodeAt();
41279                 if (
41280                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41281                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41282                     (cc >= 0xf900 && cc < 0xfb00 )
41283                 ) {
41284                         return b;
41285                 }
41286                 return "&#"+cc+";" 
41287             });
41288             if(this.fireEvent('beforesync', this, html) !== false){
41289                 this.el.dom.value = html;
41290                 this.fireEvent('sync', this, html);
41291             }
41292         }
41293     },
41294
41295     /**
41296      * Protected method that will not generally be called directly. Pushes the value of the textarea
41297      * into the iframe editor.
41298      */
41299     pushValue : function(){
41300         if(this.initialized){
41301             var v = this.el.dom.value;
41302             
41303             if(v.length < 1){
41304                 v = '&#160;';
41305             }
41306             
41307             if(this.fireEvent('beforepush', this, v) !== false){
41308                 var d = (this.doc.body || this.doc.documentElement);
41309                 d.innerHTML = v;
41310                 this.cleanUpPaste();
41311                 this.el.dom.value = d.innerHTML;
41312                 this.fireEvent('push', this, v);
41313             }
41314         }
41315     },
41316
41317     // private
41318     deferFocus : function(){
41319         this.focus.defer(10, this);
41320     },
41321
41322     // doc'ed in Field
41323     focus : function(){
41324         if(this.win && !this.sourceEditMode){
41325             this.win.focus();
41326         }else{
41327             this.el.focus();
41328         }
41329     },
41330     
41331     assignDocWin: function()
41332     {
41333         var iframe = this.iframe;
41334         
41335          if(Roo.isIE){
41336             this.doc = iframe.contentWindow.document;
41337             this.win = iframe.contentWindow;
41338         } else {
41339             if (!Roo.get(this.frameId)) {
41340                 return;
41341             }
41342             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41343             this.win = Roo.get(this.frameId).dom.contentWindow;
41344         }
41345     },
41346     
41347     // private
41348     initEditor : function(){
41349         //console.log("INIT EDITOR");
41350         this.assignDocWin();
41351         
41352         
41353         
41354         this.doc.designMode="on";
41355         this.doc.open();
41356         this.doc.write(this.getDocMarkup());
41357         this.doc.close();
41358         
41359         var dbody = (this.doc.body || this.doc.documentElement);
41360         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41361         // this copies styles from the containing element into thsi one..
41362         // not sure why we need all of this..
41363         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41364         ss['background-attachment'] = 'fixed'; // w3c
41365         dbody.bgProperties = 'fixed'; // ie
41366         Roo.DomHelper.applyStyles(dbody, ss);
41367         Roo.EventManager.on(this.doc, {
41368             //'mousedown': this.onEditorEvent,
41369             'mouseup': this.onEditorEvent,
41370             'dblclick': this.onEditorEvent,
41371             'click': this.onEditorEvent,
41372             'keyup': this.onEditorEvent,
41373             buffer:100,
41374             scope: this
41375         });
41376         if(Roo.isGecko){
41377             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41378         }
41379         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41380             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41381         }
41382         this.initialized = true;
41383
41384         this.fireEvent('initialize', this);
41385         this.pushValue();
41386     },
41387
41388     // private
41389     onDestroy : function(){
41390         
41391         
41392         
41393         if(this.rendered){
41394             
41395             for (var i =0; i < this.toolbars.length;i++) {
41396                 // fixme - ask toolbars for heights?
41397                 this.toolbars[i].onDestroy();
41398             }
41399             
41400             this.wrap.dom.innerHTML = '';
41401             this.wrap.remove();
41402         }
41403     },
41404
41405     // private
41406     onFirstFocus : function(){
41407         
41408         this.assignDocWin();
41409         
41410         
41411         this.activated = true;
41412         for (var i =0; i < this.toolbars.length;i++) {
41413             this.toolbars[i].onFirstFocus();
41414         }
41415        
41416         if(Roo.isGecko){ // prevent silly gecko errors
41417             this.win.focus();
41418             var s = this.win.getSelection();
41419             if(!s.focusNode || s.focusNode.nodeType != 3){
41420                 var r = s.getRangeAt(0);
41421                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41422                 r.collapse(true);
41423                 this.deferFocus();
41424             }
41425             try{
41426                 this.execCmd('useCSS', true);
41427                 this.execCmd('styleWithCSS', false);
41428             }catch(e){}
41429         }
41430         this.fireEvent('activate', this);
41431     },
41432
41433     // private
41434     adjustFont: function(btn){
41435         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41436         //if(Roo.isSafari){ // safari
41437         //    adjust *= 2;
41438        // }
41439         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41440         if(Roo.isSafari){ // safari
41441             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41442             v =  (v < 10) ? 10 : v;
41443             v =  (v > 48) ? 48 : v;
41444             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41445             
41446         }
41447         
41448         
41449         v = Math.max(1, v+adjust);
41450         
41451         this.execCmd('FontSize', v  );
41452     },
41453
41454     onEditorEvent : function(e){
41455         this.fireEvent('editorevent', this, e);
41456       //  this.updateToolbar();
41457         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41458     },
41459
41460     insertTag : function(tg)
41461     {
41462         // could be a bit smarter... -> wrap the current selected tRoo..
41463         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41464             
41465             range = this.createRange(this.getSelection());
41466             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41467             wrappingNode.appendChild(range.extractContents());
41468             range.insertNode(wrappingNode);
41469
41470             return;
41471             
41472             
41473             
41474         }
41475         this.execCmd("formatblock",   tg);
41476         
41477     },
41478     
41479     insertText : function(txt)
41480     {
41481         
41482         
41483         var range = this.createRange();
41484         range.deleteContents();
41485                //alert(Sender.getAttribute('label'));
41486                
41487         range.insertNode(this.doc.createTextNode(txt));
41488     } ,
41489     
41490     // private
41491     relayBtnCmd : function(btn){
41492         this.relayCmd(btn.cmd);
41493     },
41494
41495     /**
41496      * Executes a Midas editor command on the editor document and performs necessary focus and
41497      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41498      * @param {String} cmd The Midas command
41499      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41500      */
41501     relayCmd : function(cmd, value){
41502         this.win.focus();
41503         this.execCmd(cmd, value);
41504         this.fireEvent('editorevent', this);
41505         //this.updateToolbar();
41506         this.deferFocus();
41507     },
41508
41509     /**
41510      * Executes a Midas editor command directly on the editor document.
41511      * For visual commands, you should use {@link #relayCmd} instead.
41512      * <b>This should only be called after the editor is initialized.</b>
41513      * @param {String} cmd The Midas command
41514      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41515      */
41516     execCmd : function(cmd, value){
41517         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41518         this.syncValue();
41519     },
41520  
41521  
41522    
41523     /**
41524      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41525      * to insert tRoo.
41526      * @param {String} text | dom node.. 
41527      */
41528     insertAtCursor : function(text)
41529     {
41530         
41531         
41532         
41533         if(!this.activated){
41534             return;
41535         }
41536         /*
41537         if(Roo.isIE){
41538             this.win.focus();
41539             var r = this.doc.selection.createRange();
41540             if(r){
41541                 r.collapse(true);
41542                 r.pasteHTML(text);
41543                 this.syncValue();
41544                 this.deferFocus();
41545             
41546             }
41547             return;
41548         }
41549         */
41550         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41551             this.win.focus();
41552             
41553             
41554             // from jquery ui (MIT licenced)
41555             var range, node;
41556             var win = this.win;
41557             
41558             if (win.getSelection && win.getSelection().getRangeAt) {
41559                 range = win.getSelection().getRangeAt(0);
41560                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41561                 range.insertNode(node);
41562             } else if (win.document.selection && win.document.selection.createRange) {
41563                 // no firefox support
41564                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41565                 win.document.selection.createRange().pasteHTML(txt);
41566             } else {
41567                 // no firefox support
41568                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41569                 this.execCmd('InsertHTML', txt);
41570             } 
41571             
41572             this.syncValue();
41573             
41574             this.deferFocus();
41575         }
41576     },
41577  // private
41578     mozKeyPress : function(e){
41579         if(e.ctrlKey){
41580             var c = e.getCharCode(), cmd;
41581           
41582             if(c > 0){
41583                 c = String.fromCharCode(c).toLowerCase();
41584                 switch(c){
41585                     case 'b':
41586                         cmd = 'bold';
41587                         break;
41588                     case 'i':
41589                         cmd = 'italic';
41590                         break;
41591                     
41592                     case 'u':
41593                         cmd = 'underline';
41594                         break;
41595                     
41596                     case 'v':
41597                         this.cleanUpPaste.defer(100, this);
41598                         return;
41599                         
41600                 }
41601                 if(cmd){
41602                     this.win.focus();
41603                     this.execCmd(cmd);
41604                     this.deferFocus();
41605                     e.preventDefault();
41606                 }
41607                 
41608             }
41609         }
41610     },
41611
41612     // private
41613     fixKeys : function(){ // load time branching for fastest keydown performance
41614         if(Roo.isIE){
41615             return function(e){
41616                 var k = e.getKey(), r;
41617                 if(k == e.TAB){
41618                     e.stopEvent();
41619                     r = this.doc.selection.createRange();
41620                     if(r){
41621                         r.collapse(true);
41622                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41623                         this.deferFocus();
41624                     }
41625                     return;
41626                 }
41627                 
41628                 if(k == e.ENTER){
41629                     r = this.doc.selection.createRange();
41630                     if(r){
41631                         var target = r.parentElement();
41632                         if(!target || target.tagName.toLowerCase() != 'li'){
41633                             e.stopEvent();
41634                             r.pasteHTML('<br />');
41635                             r.collapse(false);
41636                             r.select();
41637                         }
41638                     }
41639                 }
41640                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41641                     this.cleanUpPaste.defer(100, this);
41642                     return;
41643                 }
41644                 
41645                 
41646             };
41647         }else if(Roo.isOpera){
41648             return function(e){
41649                 var k = e.getKey();
41650                 if(k == e.TAB){
41651                     e.stopEvent();
41652                     this.win.focus();
41653                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41654                     this.deferFocus();
41655                 }
41656                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41657                     this.cleanUpPaste.defer(100, this);
41658                     return;
41659                 }
41660                 
41661             };
41662         }else if(Roo.isSafari){
41663             return function(e){
41664                 var k = e.getKey();
41665                 
41666                 if(k == e.TAB){
41667                     e.stopEvent();
41668                     this.execCmd('InsertText','\t');
41669                     this.deferFocus();
41670                     return;
41671                 }
41672                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41673                     this.cleanUpPaste.defer(100, this);
41674                     return;
41675                 }
41676                 
41677              };
41678         }
41679     }(),
41680     
41681     getAllAncestors: function()
41682     {
41683         var p = this.getSelectedNode();
41684         var a = [];
41685         if (!p) {
41686             a.push(p); // push blank onto stack..
41687             p = this.getParentElement();
41688         }
41689         
41690         
41691         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41692             a.push(p);
41693             p = p.parentNode;
41694         }
41695         a.push(this.doc.body);
41696         return a;
41697     },
41698     lastSel : false,
41699     lastSelNode : false,
41700     
41701     
41702     getSelection : function() 
41703     {
41704         this.assignDocWin();
41705         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41706     },
41707     
41708     getSelectedNode: function() 
41709     {
41710         // this may only work on Gecko!!!
41711         
41712         // should we cache this!!!!
41713         
41714         
41715         
41716          
41717         var range = this.createRange(this.getSelection()).cloneRange();
41718         
41719         if (Roo.isIE) {
41720             var parent = range.parentElement();
41721             while (true) {
41722                 var testRange = range.duplicate();
41723                 testRange.moveToElementText(parent);
41724                 if (testRange.inRange(range)) {
41725                     break;
41726                 }
41727                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41728                     break;
41729                 }
41730                 parent = parent.parentElement;
41731             }
41732             return parent;
41733         }
41734         
41735         // is ancestor a text element.
41736         var ac =  range.commonAncestorContainer;
41737         if (ac.nodeType == 3) {
41738             ac = ac.parentNode;
41739         }
41740         
41741         var ar = ac.childNodes;
41742          
41743         var nodes = [];
41744         var other_nodes = [];
41745         var has_other_nodes = false;
41746         for (var i=0;i<ar.length;i++) {
41747             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41748                 continue;
41749             }
41750             // fullly contained node.
41751             
41752             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41753                 nodes.push(ar[i]);
41754                 continue;
41755             }
41756             
41757             // probably selected..
41758             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41759                 other_nodes.push(ar[i]);
41760                 continue;
41761             }
41762             // outer..
41763             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41764                 continue;
41765             }
41766             
41767             
41768             has_other_nodes = true;
41769         }
41770         if (!nodes.length && other_nodes.length) {
41771             nodes= other_nodes;
41772         }
41773         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41774             return false;
41775         }
41776         
41777         return nodes[0];
41778     },
41779     createRange: function(sel)
41780     {
41781         // this has strange effects when using with 
41782         // top toolbar - not sure if it's a great idea.
41783         //this.editor.contentWindow.focus();
41784         if (typeof sel != "undefined") {
41785             try {
41786                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41787             } catch(e) {
41788                 return this.doc.createRange();
41789             }
41790         } else {
41791             return this.doc.createRange();
41792         }
41793     },
41794     getParentElement: function()
41795     {
41796         
41797         this.assignDocWin();
41798         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41799         
41800         var range = this.createRange(sel);
41801          
41802         try {
41803             var p = range.commonAncestorContainer;
41804             while (p.nodeType == 3) { // text node
41805                 p = p.parentNode;
41806             }
41807             return p;
41808         } catch (e) {
41809             return null;
41810         }
41811     
41812     },
41813     /***
41814      *
41815      * Range intersection.. the hard stuff...
41816      *  '-1' = before
41817      *  '0' = hits..
41818      *  '1' = after.
41819      *         [ -- selected range --- ]
41820      *   [fail]                        [fail]
41821      *
41822      *    basically..
41823      *      if end is before start or  hits it. fail.
41824      *      if start is after end or hits it fail.
41825      *
41826      *   if either hits (but other is outside. - then it's not 
41827      *   
41828      *    
41829      **/
41830     
41831     
41832     // @see http://www.thismuchiknow.co.uk/?p=64.
41833     rangeIntersectsNode : function(range, node)
41834     {
41835         var nodeRange = node.ownerDocument.createRange();
41836         try {
41837             nodeRange.selectNode(node);
41838         } catch (e) {
41839             nodeRange.selectNodeContents(node);
41840         }
41841     
41842         var rangeStartRange = range.cloneRange();
41843         rangeStartRange.collapse(true);
41844     
41845         var rangeEndRange = range.cloneRange();
41846         rangeEndRange.collapse(false);
41847     
41848         var nodeStartRange = nodeRange.cloneRange();
41849         nodeStartRange.collapse(true);
41850     
41851         var nodeEndRange = nodeRange.cloneRange();
41852         nodeEndRange.collapse(false);
41853     
41854         return rangeStartRange.compareBoundaryPoints(
41855                  Range.START_TO_START, nodeEndRange) == -1 &&
41856                rangeEndRange.compareBoundaryPoints(
41857                  Range.START_TO_START, nodeStartRange) == 1;
41858         
41859          
41860     },
41861     rangeCompareNode : function(range, node)
41862     {
41863         var nodeRange = node.ownerDocument.createRange();
41864         try {
41865             nodeRange.selectNode(node);
41866         } catch (e) {
41867             nodeRange.selectNodeContents(node);
41868         }
41869         
41870         
41871         range.collapse(true);
41872     
41873         nodeRange.collapse(true);
41874      
41875         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41876         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41877          
41878         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41879         
41880         var nodeIsBefore   =  ss == 1;
41881         var nodeIsAfter    = ee == -1;
41882         
41883         if (nodeIsBefore && nodeIsAfter)
41884             return 0; // outer
41885         if (!nodeIsBefore && nodeIsAfter)
41886             return 1; //right trailed.
41887         
41888         if (nodeIsBefore && !nodeIsAfter)
41889             return 2;  // left trailed.
41890         // fully contined.
41891         return 3;
41892     },
41893
41894     // private? - in a new class?
41895     cleanUpPaste :  function()
41896     {
41897         // cleans up the whole document..
41898          Roo.log('cleanuppaste');
41899         this.cleanUpChildren(this.doc.body);
41900         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41901         if (clean != this.doc.body.innerHTML) {
41902             this.doc.body.innerHTML = clean;
41903         }
41904         
41905     },
41906     
41907     cleanWordChars : function(input) {// change the chars to hex code
41908         var he = Roo.form.HtmlEditor;
41909         
41910         var output = input;
41911         Roo.each(he.swapCodes, function(sw) { 
41912             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41913             
41914             output = output.replace(swapper, sw[1]);
41915         });
41916         
41917         return output;
41918     },
41919     
41920     
41921     cleanUpChildren : function (n)
41922     {
41923         if (!n.childNodes.length) {
41924             return;
41925         }
41926         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41927            this.cleanUpChild(n.childNodes[i]);
41928         }
41929     },
41930     
41931     
41932         
41933     
41934     cleanUpChild : function (node)
41935     {
41936         var ed = this;
41937         //console.log(node);
41938         if (node.nodeName == "#text") {
41939             // clean up silly Windows -- stuff?
41940             return; 
41941         }
41942         if (node.nodeName == "#comment") {
41943             node.parentNode.removeChild(node);
41944             // clean up silly Windows -- stuff?
41945             return; 
41946         }
41947         
41948         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41949             // remove node.
41950             node.parentNode.removeChild(node);
41951             return;
41952             
41953         }
41954         
41955         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41956         
41957         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41958         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41959         
41960         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41961         //    remove_keep_children = true;
41962         //}
41963         
41964         if (remove_keep_children) {
41965             this.cleanUpChildren(node);
41966             // inserts everything just before this node...
41967             while (node.childNodes.length) {
41968                 var cn = node.childNodes[0];
41969                 node.removeChild(cn);
41970                 node.parentNode.insertBefore(cn, node);
41971             }
41972             node.parentNode.removeChild(node);
41973             return;
41974         }
41975         
41976         if (!node.attributes || !node.attributes.length) {
41977             this.cleanUpChildren(node);
41978             return;
41979         }
41980         
41981         function cleanAttr(n,v)
41982         {
41983             
41984             if (v.match(/^\./) || v.match(/^\//)) {
41985                 return;
41986             }
41987             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41988                 return;
41989             }
41990             if (v.match(/^#/)) {
41991                 return;
41992             }
41993 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41994             node.removeAttribute(n);
41995             
41996         }
41997         
41998         function cleanStyle(n,v)
41999         {
42000             if (v.match(/expression/)) { //XSS?? should we even bother..
42001                 node.removeAttribute(n);
42002                 return;
42003             }
42004             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
42005             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
42006             
42007             
42008             var parts = v.split(/;/);
42009             var clean = [];
42010             
42011             Roo.each(parts, function(p) {
42012                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42013                 if (!p.length) {
42014                     return true;
42015                 }
42016                 var l = p.split(':').shift().replace(/\s+/g,'');
42017                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42018                 
42019                 
42020                 if ( cblack.indexOf(l) > -1) {
42021 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42022                     //node.removeAttribute(n);
42023                     return true;
42024                 }
42025                 //Roo.log()
42026                 // only allow 'c whitelisted system attributes'
42027                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42028 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42029                     //node.removeAttribute(n);
42030                     return true;
42031                 }
42032                 
42033                 
42034                  
42035                 
42036                 clean.push(p);
42037                 return true;
42038             });
42039             if (clean.length) { 
42040                 node.setAttribute(n, clean.join(';'));
42041             } else {
42042                 node.removeAttribute(n);
42043             }
42044             
42045         }
42046         
42047         
42048         for (var i = node.attributes.length-1; i > -1 ; i--) {
42049             var a = node.attributes[i];
42050             //console.log(a);
42051             
42052             if (a.name.toLowerCase().substr(0,2)=='on')  {
42053                 node.removeAttribute(a.name);
42054                 continue;
42055             }
42056             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
42057                 node.removeAttribute(a.name);
42058                 continue;
42059             }
42060             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
42061                 cleanAttr(a.name,a.value); // fixme..
42062                 continue;
42063             }
42064             if (a.name == 'style') {
42065                 cleanStyle(a.name,a.value);
42066                 continue;
42067             }
42068             /// clean up MS crap..
42069             // tecnically this should be a list of valid class'es..
42070             
42071             
42072             if (a.name == 'class') {
42073                 if (a.value.match(/^Mso/)) {
42074                     node.className = '';
42075                 }
42076                 
42077                 if (a.value.match(/body/)) {
42078                     node.className = '';
42079                 }
42080                 continue;
42081             }
42082             
42083             // style cleanup!?
42084             // class cleanup?
42085             
42086         }
42087         
42088         
42089         this.cleanUpChildren(node);
42090         
42091         
42092     }
42093     
42094     
42095     // hide stuff that is not compatible
42096     /**
42097      * @event blur
42098      * @hide
42099      */
42100     /**
42101      * @event change
42102      * @hide
42103      */
42104     /**
42105      * @event focus
42106      * @hide
42107      */
42108     /**
42109      * @event specialkey
42110      * @hide
42111      */
42112     /**
42113      * @cfg {String} fieldClass @hide
42114      */
42115     /**
42116      * @cfg {String} focusClass @hide
42117      */
42118     /**
42119      * @cfg {String} autoCreate @hide
42120      */
42121     /**
42122      * @cfg {String} inputType @hide
42123      */
42124     /**
42125      * @cfg {String} invalidClass @hide
42126      */
42127     /**
42128      * @cfg {String} invalidText @hide
42129      */
42130     /**
42131      * @cfg {String} msgFx @hide
42132      */
42133     /**
42134      * @cfg {String} validateOnBlur @hide
42135      */
42136 });
42137
42138 Roo.form.HtmlEditor.white = [
42139         'area', 'br', 'img', 'input', 'hr', 'wbr',
42140         
42141        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42142        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42143        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42144        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42145        'table',   'ul',         'xmp', 
42146        
42147        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42148       'thead',   'tr', 
42149      
42150       'dir', 'menu', 'ol', 'ul', 'dl',
42151        
42152       'embed',  'object'
42153 ];
42154
42155
42156 Roo.form.HtmlEditor.black = [
42157     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42158         'applet', // 
42159         'base',   'basefont', 'bgsound', 'blink',  'body', 
42160         'frame',  'frameset', 'head',    'html',   'ilayer', 
42161         'iframe', 'layer',  'link',     'meta',    'object',   
42162         'script', 'style' ,'title',  'xml' // clean later..
42163 ];
42164 Roo.form.HtmlEditor.clean = [
42165     'script', 'style', 'title', 'xml'
42166 ];
42167 Roo.form.HtmlEditor.remove = [
42168     'font'
42169 ];
42170 // attributes..
42171
42172 Roo.form.HtmlEditor.ablack = [
42173     'on'
42174 ];
42175     
42176 Roo.form.HtmlEditor.aclean = [ 
42177     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42178 ];
42179
42180 // protocols..
42181 Roo.form.HtmlEditor.pwhite= [
42182         'http',  'https',  'mailto'
42183 ];
42184
42185 // white listed style attributes.
42186 Roo.form.HtmlEditor.cwhite= [
42187       //  'text-align', /// default is to allow most things..
42188       
42189          
42190 //        'font-size'//??
42191 ];
42192
42193 // black listed style attributes.
42194 Roo.form.HtmlEditor.cblack= [
42195       //  'font-size' -- this can be set by the project 
42196 ];
42197
42198
42199 Roo.form.HtmlEditor.swapCodes   =[ 
42200     [    8211, "--" ], 
42201     [    8212, "--" ], 
42202     [    8216,  "'" ],  
42203     [    8217, "'" ],  
42204     [    8220, '"' ],  
42205     [    8221, '"' ],  
42206     [    8226, "*" ],  
42207     [    8230, "..." ]
42208 ]; 
42209
42210     // <script type="text/javascript">
42211 /*
42212  * Based on
42213  * Ext JS Library 1.1.1
42214  * Copyright(c) 2006-2007, Ext JS, LLC.
42215  *  
42216  
42217  */
42218
42219 /**
42220  * @class Roo.form.HtmlEditorToolbar1
42221  * Basic Toolbar
42222  * 
42223  * Usage:
42224  *
42225  new Roo.form.HtmlEditor({
42226     ....
42227     toolbars : [
42228         new Roo.form.HtmlEditorToolbar1({
42229             disable : { fonts: 1 , format: 1, ..., ... , ...],
42230             btns : [ .... ]
42231         })
42232     }
42233      
42234  * 
42235  * @cfg {Object} disable List of elements to disable..
42236  * @cfg {Array} btns List of additional buttons.
42237  * 
42238  * 
42239  * NEEDS Extra CSS? 
42240  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42241  */
42242  
42243 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42244 {
42245     
42246     Roo.apply(this, config);
42247     
42248     // default disabled, based on 'good practice'..
42249     this.disable = this.disable || {};
42250     Roo.applyIf(this.disable, {
42251         fontSize : true,
42252         colors : true,
42253         specialElements : true
42254     });
42255     
42256     
42257     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42258     // dont call parent... till later.
42259 }
42260
42261 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42262     
42263     tb: false,
42264     
42265     rendered: false,
42266     
42267     editor : false,
42268     /**
42269      * @cfg {Object} disable  List of toolbar elements to disable
42270          
42271      */
42272     disable : false,
42273       /**
42274      * @cfg {Array} fontFamilies An array of available font families
42275      */
42276     fontFamilies : [
42277         'Arial',
42278         'Courier New',
42279         'Tahoma',
42280         'Times New Roman',
42281         'Verdana'
42282     ],
42283     
42284     specialChars : [
42285            "&#169;",
42286           "&#174;",     
42287           "&#8482;",    
42288           "&#163;" ,    
42289          // "&#8212;",    
42290           "&#8230;",    
42291           "&#247;" ,    
42292         //  "&#225;" ,     ?? a acute?
42293            "&#8364;"    , //Euro
42294        //   "&#8220;"    ,
42295         //  "&#8221;"    ,
42296         //  "&#8226;"    ,
42297           "&#176;"  //   , // degrees
42298
42299          // "&#233;"     , // e ecute
42300          // "&#250;"     , // u ecute?
42301     ],
42302     
42303     specialElements : [
42304         {
42305             text: "Insert Table",
42306             xtype: 'MenuItem',
42307             xns : Roo.Menu,
42308             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42309                 
42310         },
42311         {    
42312             text: "Insert Image",
42313             xtype: 'MenuItem',
42314             xns : Roo.Menu,
42315             ihtml : '<img src="about:blank"/>'
42316             
42317         }
42318         
42319          
42320     ],
42321     
42322     
42323     inputElements : [ 
42324             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42325             "input:submit", "input:button", "select", "textarea", "label" ],
42326     formats : [
42327         ["p"] ,  
42328         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42329         ["pre"],[ "code"], 
42330         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42331         ['div'],['span']
42332     ],
42333     
42334     cleanStyles : [
42335         "font-size"
42336     ],
42337      /**
42338      * @cfg {String} defaultFont default font to use.
42339      */
42340     defaultFont: 'tahoma',
42341    
42342     fontSelect : false,
42343     
42344     
42345     formatCombo : false,
42346     
42347     init : function(editor)
42348     {
42349         this.editor = editor;
42350         
42351         
42352         var fid = editor.frameId;
42353         var etb = this;
42354         function btn(id, toggle, handler){
42355             var xid = fid + '-'+ id ;
42356             return {
42357                 id : xid,
42358                 cmd : id,
42359                 cls : 'x-btn-icon x-edit-'+id,
42360                 enableToggle:toggle !== false,
42361                 scope: editor, // was editor...
42362                 handler:handler||editor.relayBtnCmd,
42363                 clickEvent:'mousedown',
42364                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42365                 tabIndex:-1
42366             };
42367         }
42368         
42369         
42370         
42371         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42372         this.tb = tb;
42373          // stop form submits
42374         tb.el.on('click', function(e){
42375             e.preventDefault(); // what does this do?
42376         });
42377
42378         if(!this.disable.font) { // && !Roo.isSafari){
42379             /* why no safari for fonts 
42380             editor.fontSelect = tb.el.createChild({
42381                 tag:'select',
42382                 tabIndex: -1,
42383                 cls:'x-font-select',
42384                 html: this.createFontOptions()
42385             });
42386             
42387             editor.fontSelect.on('change', function(){
42388                 var font = editor.fontSelect.dom.value;
42389                 editor.relayCmd('fontname', font);
42390                 editor.deferFocus();
42391             }, editor);
42392             
42393             tb.add(
42394                 editor.fontSelect.dom,
42395                 '-'
42396             );
42397             */
42398             
42399         };
42400         if(!this.disable.formats){
42401             this.formatCombo = new Roo.form.ComboBox({
42402                 store: new Roo.data.SimpleStore({
42403                     id : 'tag',
42404                     fields: ['tag'],
42405                     data : this.formats // from states.js
42406                 }),
42407                 blockFocus : true,
42408                 name : '',
42409                 //autoCreate : {tag: "div",  size: "20"},
42410                 displayField:'tag',
42411                 typeAhead: false,
42412                 mode: 'local',
42413                 editable : false,
42414                 triggerAction: 'all',
42415                 emptyText:'Add tag',
42416                 selectOnFocus:true,
42417                 width:135,
42418                 listeners : {
42419                     'select': function(c, r, i) {
42420                         editor.insertTag(r.get('tag'));
42421                         editor.focus();
42422                     }
42423                 }
42424
42425             });
42426             tb.addField(this.formatCombo);
42427             
42428         }
42429         
42430         if(!this.disable.format){
42431             tb.add(
42432                 btn('bold'),
42433                 btn('italic'),
42434                 btn('underline')
42435             );
42436         };
42437         if(!this.disable.fontSize){
42438             tb.add(
42439                 '-',
42440                 
42441                 
42442                 btn('increasefontsize', false, editor.adjustFont),
42443                 btn('decreasefontsize', false, editor.adjustFont)
42444             );
42445         };
42446         
42447         
42448         if(!this.disable.colors){
42449             tb.add(
42450                 '-', {
42451                     id:editor.frameId +'-forecolor',
42452                     cls:'x-btn-icon x-edit-forecolor',
42453                     clickEvent:'mousedown',
42454                     tooltip: this.buttonTips['forecolor'] || undefined,
42455                     tabIndex:-1,
42456                     menu : new Roo.menu.ColorMenu({
42457                         allowReselect: true,
42458                         focus: Roo.emptyFn,
42459                         value:'000000',
42460                         plain:true,
42461                         selectHandler: function(cp, color){
42462                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42463                             editor.deferFocus();
42464                         },
42465                         scope: editor,
42466                         clickEvent:'mousedown'
42467                     })
42468                 }, {
42469                     id:editor.frameId +'backcolor',
42470                     cls:'x-btn-icon x-edit-backcolor',
42471                     clickEvent:'mousedown',
42472                     tooltip: this.buttonTips['backcolor'] || undefined,
42473                     tabIndex:-1,
42474                     menu : new Roo.menu.ColorMenu({
42475                         focus: Roo.emptyFn,
42476                         value:'FFFFFF',
42477                         plain:true,
42478                         allowReselect: true,
42479                         selectHandler: function(cp, color){
42480                             if(Roo.isGecko){
42481                                 editor.execCmd('useCSS', false);
42482                                 editor.execCmd('hilitecolor', color);
42483                                 editor.execCmd('useCSS', true);
42484                                 editor.deferFocus();
42485                             }else{
42486                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42487                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42488                                 editor.deferFocus();
42489                             }
42490                         },
42491                         scope:editor,
42492                         clickEvent:'mousedown'
42493                     })
42494                 }
42495             );
42496         };
42497         // now add all the items...
42498         
42499
42500         if(!this.disable.alignments){
42501             tb.add(
42502                 '-',
42503                 btn('justifyleft'),
42504                 btn('justifycenter'),
42505                 btn('justifyright')
42506             );
42507         };
42508
42509         //if(!Roo.isSafari){
42510             if(!this.disable.links){
42511                 tb.add(
42512                     '-',
42513                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
42514                 );
42515             };
42516
42517             if(!this.disable.lists){
42518                 tb.add(
42519                     '-',
42520                     btn('insertorderedlist'),
42521                     btn('insertunorderedlist')
42522                 );
42523             }
42524             if(!this.disable.sourceEdit){
42525                 tb.add(
42526                     '-',
42527                     btn('sourceedit', true, function(btn){
42528                         this.toggleSourceEdit(btn.pressed);
42529                     })
42530                 );
42531             }
42532         //}
42533         
42534         var smenu = { };
42535         // special menu.. - needs to be tidied up..
42536         if (!this.disable.special) {
42537             smenu = {
42538                 text: "&#169;",
42539                 cls: 'x-edit-none',
42540                 
42541                 menu : {
42542                     items : []
42543                 }
42544             };
42545             for (var i =0; i < this.specialChars.length; i++) {
42546                 smenu.menu.items.push({
42547                     
42548                     html: this.specialChars[i],
42549                     handler: function(a,b) {
42550                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42551                         //editor.insertAtCursor(a.html);
42552                         
42553                     },
42554                     tabIndex:-1
42555                 });
42556             }
42557             
42558             
42559             tb.add(smenu);
42560             
42561             
42562         }
42563         
42564         var cmenu = { };
42565         if (!this.disable.cleanStyles) {
42566             cmenu = {
42567                 cls: 'x-btn-icon x-btn-clear',
42568                 
42569                 menu : {
42570                     items : []
42571                 }
42572             };
42573             for (var i =0; i < this.cleanStyles.length; i++) {
42574                 cmenu.menu.items.push({
42575                     actiontype : this.cleanStyles[i],
42576                     html: 'Remove ' + this.cleanStyles[i],
42577                     handler: function(a,b) {
42578                         Roo.log(a);
42579                         Roo.log(b);
42580                         var c = Roo.get(editor.doc.body);
42581                         c.select('[style]').each(function(s) {
42582                             s.dom.style.removeProperty(a.actiontype);
42583                         });
42584                         
42585                     },
42586                     tabIndex:-1
42587                 });
42588             }
42589             
42590             tb.add(cmenu);
42591         }
42592          
42593         if (!this.disable.specialElements) {
42594             var semenu = {
42595                 text: "Other;",
42596                 cls: 'x-edit-none',
42597                 menu : {
42598                     items : []
42599                 }
42600             };
42601             for (var i =0; i < this.specialElements.length; i++) {
42602                 semenu.menu.items.push(
42603                     Roo.apply({ 
42604                         handler: function(a,b) {
42605                             editor.insertAtCursor(this.ihtml);
42606                         }
42607                     }, this.specialElements[i])
42608                 );
42609                     
42610             }
42611             
42612             tb.add(semenu);
42613             
42614             
42615         }
42616          
42617         
42618         if (this.btns) {
42619             for(var i =0; i< this.btns.length;i++) {
42620                 var b = Roo.factory(this.btns[i],Roo.form);
42621                 b.cls =  'x-edit-none';
42622                 b.scope = editor;
42623                 tb.add(b);
42624             }
42625         
42626         }
42627         
42628         
42629         
42630         // disable everything...
42631         
42632         this.tb.items.each(function(item){
42633            if(item.id != editor.frameId+ '-sourceedit'){
42634                 item.disable();
42635             }
42636         });
42637         this.rendered = true;
42638         
42639         // the all the btns;
42640         editor.on('editorevent', this.updateToolbar, this);
42641         // other toolbars need to implement this..
42642         //editor.on('editmodechange', this.updateToolbar, this);
42643     },
42644     
42645     
42646     
42647     /**
42648      * Protected method that will not generally be called directly. It triggers
42649      * a toolbar update by reading the markup state of the current selection in the editor.
42650      */
42651     updateToolbar: function(){
42652
42653         if(!this.editor.activated){
42654             this.editor.onFirstFocus();
42655             return;
42656         }
42657
42658         var btns = this.tb.items.map, 
42659             doc = this.editor.doc,
42660             frameId = this.editor.frameId;
42661
42662         if(!this.disable.font && !Roo.isSafari){
42663             /*
42664             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42665             if(name != this.fontSelect.dom.value){
42666                 this.fontSelect.dom.value = name;
42667             }
42668             */
42669         }
42670         if(!this.disable.format){
42671             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42672             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42673             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42674         }
42675         if(!this.disable.alignments){
42676             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42677             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42678             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42679         }
42680         if(!Roo.isSafari && !this.disable.lists){
42681             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42682             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42683         }
42684         
42685         var ans = this.editor.getAllAncestors();
42686         if (this.formatCombo) {
42687             
42688             
42689             var store = this.formatCombo.store;
42690             this.formatCombo.setValue("");
42691             for (var i =0; i < ans.length;i++) {
42692                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42693                     // select it..
42694                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42695                     break;
42696                 }
42697             }
42698         }
42699         
42700         
42701         
42702         // hides menus... - so this cant be on a menu...
42703         Roo.menu.MenuMgr.hideAll();
42704
42705         //this.editorsyncValue();
42706     },
42707    
42708     
42709     createFontOptions : function(){
42710         var buf = [], fs = this.fontFamilies, ff, lc;
42711         
42712         
42713         
42714         for(var i = 0, len = fs.length; i< len; i++){
42715             ff = fs[i];
42716             lc = ff.toLowerCase();
42717             buf.push(
42718                 '<option value="',lc,'" style="font-family:',ff,';"',
42719                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42720                     ff,
42721                 '</option>'
42722             );
42723         }
42724         return buf.join('');
42725     },
42726     
42727     toggleSourceEdit : function(sourceEditMode){
42728         if(sourceEditMode === undefined){
42729             sourceEditMode = !this.sourceEditMode;
42730         }
42731         this.sourceEditMode = sourceEditMode === true;
42732         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42733         // just toggle the button?
42734         if(btn.pressed !== this.editor.sourceEditMode){
42735             btn.toggle(this.editor.sourceEditMode);
42736             return;
42737         }
42738         
42739         if(this.sourceEditMode){
42740             this.tb.items.each(function(item){
42741                 if(item.cmd != 'sourceedit'){
42742                     item.disable();
42743                 }
42744             });
42745           
42746         }else{
42747             if(this.initialized){
42748                 this.tb.items.each(function(item){
42749                     item.enable();
42750                 });
42751             }
42752             
42753         }
42754         // tell the editor that it's been pressed..
42755         this.editor.toggleSourceEdit(sourceEditMode);
42756        
42757     },
42758      /**
42759      * Object collection of toolbar tooltips for the buttons in the editor. The key
42760      * is the command id associated with that button and the value is a valid QuickTips object.
42761      * For example:
42762 <pre><code>
42763 {
42764     bold : {
42765         title: 'Bold (Ctrl+B)',
42766         text: 'Make the selected text bold.',
42767         cls: 'x-html-editor-tip'
42768     },
42769     italic : {
42770         title: 'Italic (Ctrl+I)',
42771         text: 'Make the selected text italic.',
42772         cls: 'x-html-editor-tip'
42773     },
42774     ...
42775 </code></pre>
42776     * @type Object
42777      */
42778     buttonTips : {
42779         bold : {
42780             title: 'Bold (Ctrl+B)',
42781             text: 'Make the selected text bold.',
42782             cls: 'x-html-editor-tip'
42783         },
42784         italic : {
42785             title: 'Italic (Ctrl+I)',
42786             text: 'Make the selected text italic.',
42787             cls: 'x-html-editor-tip'
42788         },
42789         underline : {
42790             title: 'Underline (Ctrl+U)',
42791             text: 'Underline the selected text.',
42792             cls: 'x-html-editor-tip'
42793         },
42794         increasefontsize : {
42795             title: 'Grow Text',
42796             text: 'Increase the font size.',
42797             cls: 'x-html-editor-tip'
42798         },
42799         decreasefontsize : {
42800             title: 'Shrink Text',
42801             text: 'Decrease the font size.',
42802             cls: 'x-html-editor-tip'
42803         },
42804         backcolor : {
42805             title: 'Text Highlight Color',
42806             text: 'Change the background color of the selected text.',
42807             cls: 'x-html-editor-tip'
42808         },
42809         forecolor : {
42810             title: 'Font Color',
42811             text: 'Change the color of the selected text.',
42812             cls: 'x-html-editor-tip'
42813         },
42814         justifyleft : {
42815             title: 'Align Text Left',
42816             text: 'Align text to the left.',
42817             cls: 'x-html-editor-tip'
42818         },
42819         justifycenter : {
42820             title: 'Center Text',
42821             text: 'Center text in the editor.',
42822             cls: 'x-html-editor-tip'
42823         },
42824         justifyright : {
42825             title: 'Align Text Right',
42826             text: 'Align text to the right.',
42827             cls: 'x-html-editor-tip'
42828         },
42829         insertunorderedlist : {
42830             title: 'Bullet List',
42831             text: 'Start a bulleted list.',
42832             cls: 'x-html-editor-tip'
42833         },
42834         insertorderedlist : {
42835             title: 'Numbered List',
42836             text: 'Start a numbered list.',
42837             cls: 'x-html-editor-tip'
42838         },
42839         createlink : {
42840             title: 'Hyperlink',
42841             text: 'Make the selected text a hyperlink.',
42842             cls: 'x-html-editor-tip'
42843         },
42844         sourceedit : {
42845             title: 'Source Edit',
42846             text: 'Switch to source editing mode.',
42847             cls: 'x-html-editor-tip'
42848         }
42849     },
42850     // private
42851     onDestroy : function(){
42852         if(this.rendered){
42853             
42854             this.tb.items.each(function(item){
42855                 if(item.menu){
42856                     item.menu.removeAll();
42857                     if(item.menu.el){
42858                         item.menu.el.destroy();
42859                     }
42860                 }
42861                 item.destroy();
42862             });
42863              
42864         }
42865     },
42866     onFirstFocus: function() {
42867         this.tb.items.each(function(item){
42868            item.enable();
42869         });
42870     }
42871 });
42872
42873
42874
42875
42876 // <script type="text/javascript">
42877 /*
42878  * Based on
42879  * Ext JS Library 1.1.1
42880  * Copyright(c) 2006-2007, Ext JS, LLC.
42881  *  
42882  
42883  */
42884
42885  
42886 /**
42887  * @class Roo.form.HtmlEditor.ToolbarContext
42888  * Context Toolbar
42889  * 
42890  * Usage:
42891  *
42892  new Roo.form.HtmlEditor({
42893     ....
42894     toolbars : [
42895         { xtype: 'ToolbarStandard', styles : {} }
42896         { xtype: 'ToolbarContext', disable : {} }
42897     ]
42898 })
42899
42900      
42901  * 
42902  * @config : {Object} disable List of elements to disable.. (not done yet.)
42903  * @config : {Object} styles  Map of styles available.
42904  * 
42905  */
42906
42907 Roo.form.HtmlEditor.ToolbarContext = function(config)
42908 {
42909     
42910     Roo.apply(this, config);
42911     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42912     // dont call parent... till later.
42913     this.styles = this.styles || {};
42914 }
42915
42916  
42917
42918 Roo.form.HtmlEditor.ToolbarContext.types = {
42919     'IMG' : {
42920         width : {
42921             title: "Width",
42922             width: 40
42923         },
42924         height:  {
42925             title: "Height",
42926             width: 40
42927         },
42928         align: {
42929             title: "Align",
42930             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42931             width : 80
42932             
42933         },
42934         border: {
42935             title: "Border",
42936             width: 40
42937         },
42938         alt: {
42939             title: "Alt",
42940             width: 120
42941         },
42942         src : {
42943             title: "Src",
42944             width: 220
42945         }
42946         
42947     },
42948     'A' : {
42949         name : {
42950             title: "Name",
42951             width: 50
42952         },
42953         target:  {
42954             title: "Target",
42955             width: 120
42956         },
42957         href:  {
42958             title: "Href",
42959             width: 220
42960         } // border?
42961         
42962     },
42963     'TABLE' : {
42964         rows : {
42965             title: "Rows",
42966             width: 20
42967         },
42968         cols : {
42969             title: "Cols",
42970             width: 20
42971         },
42972         width : {
42973             title: "Width",
42974             width: 40
42975         },
42976         height : {
42977             title: "Height",
42978             width: 40
42979         },
42980         border : {
42981             title: "Border",
42982             width: 20
42983         }
42984     },
42985     'TD' : {
42986         width : {
42987             title: "Width",
42988             width: 40
42989         },
42990         height : {
42991             title: "Height",
42992             width: 40
42993         },   
42994         align: {
42995             title: "Align",
42996             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42997             width: 80
42998         },
42999         valign: {
43000             title: "Valign",
43001             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43002             width: 80
43003         },
43004         colspan: {
43005             title: "Colspan",
43006             width: 20
43007             
43008         },
43009          'font-family'  : {
43010             title : "Font",
43011             style : 'fontFamily',
43012             displayField: 'display',
43013             optname : 'font-family',
43014             width: 140
43015         }
43016     },
43017     'INPUT' : {
43018         name : {
43019             title: "name",
43020             width: 120
43021         },
43022         value : {
43023             title: "Value",
43024             width: 120
43025         },
43026         width : {
43027             title: "Width",
43028             width: 40
43029         }
43030     },
43031     'LABEL' : {
43032         'for' : {
43033             title: "For",
43034             width: 120
43035         }
43036     },
43037     'TEXTAREA' : {
43038           name : {
43039             title: "name",
43040             width: 120
43041         },
43042         rows : {
43043             title: "Rows",
43044             width: 20
43045         },
43046         cols : {
43047             title: "Cols",
43048             width: 20
43049         }
43050     },
43051     'SELECT' : {
43052         name : {
43053             title: "name",
43054             width: 120
43055         },
43056         selectoptions : {
43057             title: "Options",
43058             width: 200
43059         }
43060     },
43061     
43062     // should we really allow this??
43063     // should this just be 
43064     'BODY' : {
43065         title : {
43066             title: "Title",
43067             width: 200,
43068             disabled : true
43069         }
43070     },
43071     'SPAN' : {
43072         'font-family'  : {
43073             title : "Font",
43074             style : 'fontFamily',
43075             displayField: 'display',
43076             optname : 'font-family',
43077             width: 140
43078         }
43079     },
43080     'DIV' : {
43081         'font-family'  : {
43082             title : "Font",
43083             style : 'fontFamily',
43084             displayField: 'display',
43085             optname : 'font-family',
43086             width: 140
43087         }
43088     },
43089      'P' : {
43090         'font-family'  : {
43091             title : "Font",
43092             style : 'fontFamily',
43093             displayField: 'display',
43094             optname : 'font-family',
43095             width: 140
43096         }
43097     },
43098     
43099     '*' : {
43100         // empty..
43101     }
43102
43103 };
43104
43105 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43106 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43107
43108 Roo.form.HtmlEditor.ToolbarContext.options = {
43109         'font-family'  : [ 
43110                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43111                 [ 'Courier New', 'Courier New'],
43112                 [ 'Tahoma', 'Tahoma'],
43113                 [ 'Times New Roman,serif', 'Times'],
43114                 [ 'Verdana','Verdana' ]
43115         ]
43116 };
43117
43118 // fixme - these need to be configurable..
43119  
43120
43121 Roo.form.HtmlEditor.ToolbarContext.types
43122
43123
43124 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43125     
43126     tb: false,
43127     
43128     rendered: false,
43129     
43130     editor : false,
43131     /**
43132      * @cfg {Object} disable  List of toolbar elements to disable
43133          
43134      */
43135     disable : false,
43136     /**
43137      * @cfg {Object} styles List of styles 
43138      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43139      *
43140      * These must be defined in the page, so they get rendered correctly..
43141      * .headline { }
43142      * TD.underline { }
43143      * 
43144      */
43145     styles : false,
43146     
43147     options: false,
43148     
43149     toolbars : false,
43150     
43151     init : function(editor)
43152     {
43153         this.editor = editor;
43154         
43155         
43156         var fid = editor.frameId;
43157         var etb = this;
43158         function btn(id, toggle, handler){
43159             var xid = fid + '-'+ id ;
43160             return {
43161                 id : xid,
43162                 cmd : id,
43163                 cls : 'x-btn-icon x-edit-'+id,
43164                 enableToggle:toggle !== false,
43165                 scope: editor, // was editor...
43166                 handler:handler||editor.relayBtnCmd,
43167                 clickEvent:'mousedown',
43168                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43169                 tabIndex:-1
43170             };
43171         }
43172         // create a new element.
43173         var wdiv = editor.wrap.createChild({
43174                 tag: 'div'
43175             }, editor.wrap.dom.firstChild.nextSibling, true);
43176         
43177         // can we do this more than once??
43178         
43179          // stop form submits
43180       
43181  
43182         // disable everything...
43183         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43184         this.toolbars = {};
43185            
43186         for (var i in  ty) {
43187           
43188             this.toolbars[i] = this.buildToolbar(ty[i],i);
43189         }
43190         this.tb = this.toolbars.BODY;
43191         this.tb.el.show();
43192         this.buildFooter();
43193         this.footer.show();
43194         editor.on('hide', function( ) { this.footer.hide() }, this);
43195         editor.on('show', function( ) { this.footer.show() }, this);
43196         
43197          
43198         this.rendered = true;
43199         
43200         // the all the btns;
43201         editor.on('editorevent', this.updateToolbar, this);
43202         // other toolbars need to implement this..
43203         //editor.on('editmodechange', this.updateToolbar, this);
43204     },
43205     
43206     
43207     
43208     /**
43209      * Protected method that will not generally be called directly. It triggers
43210      * a toolbar update by reading the markup state of the current selection in the editor.
43211      */
43212     updateToolbar: function(editor,ev,sel){
43213
43214         //Roo.log(ev);
43215         // capture mouse up - this is handy for selecting images..
43216         // perhaps should go somewhere else...
43217         if(!this.editor.activated){
43218              this.editor.onFirstFocus();
43219             return;
43220         }
43221         
43222         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43223         // selectNode - might want to handle IE?
43224         if (ev &&
43225             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43226             ev.target && ev.target.tagName == 'IMG') {
43227             // they have click on an image...
43228             // let's see if we can change the selection...
43229             sel = ev.target;
43230          
43231               var nodeRange = sel.ownerDocument.createRange();
43232             try {
43233                 nodeRange.selectNode(sel);
43234             } catch (e) {
43235                 nodeRange.selectNodeContents(sel);
43236             }
43237             //nodeRange.collapse(true);
43238             var s = editor.win.getSelection();
43239             s.removeAllRanges();
43240             s.addRange(nodeRange);
43241         }  
43242         
43243       
43244         var updateFooter = sel ? false : true;
43245         
43246         
43247         var ans = this.editor.getAllAncestors();
43248         
43249         // pick
43250         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43251         
43252         if (!sel) { 
43253             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
43254             sel = sel ? sel : this.editor.doc.body;
43255             sel = sel.tagName.length ? sel : this.editor.doc.body;
43256             
43257         }
43258         // pick a menu that exists..
43259         var tn = sel.tagName.toUpperCase();
43260         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43261         
43262         tn = sel.tagName.toUpperCase();
43263         
43264         var lastSel = this.tb.selectedNode
43265         
43266         this.tb.selectedNode = sel;
43267         
43268         // if current menu does not match..
43269         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43270                 
43271             this.tb.el.hide();
43272             ///console.log("show: " + tn);
43273             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43274             this.tb.el.show();
43275             // update name
43276             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43277             
43278             
43279             // update attributes
43280             if (this.tb.fields) {
43281                 this.tb.fields.each(function(e) {
43282                     if (e.stylename) {
43283                         e.setValue(sel.style[e.stylename]);
43284                         return;
43285                     } 
43286                    e.setValue(sel.getAttribute(e.attrname));
43287                 });
43288             }
43289             
43290             var hasStyles = false;
43291             for(var i in this.styles) {
43292                 hasStyles = true;
43293                 break;
43294             }
43295             
43296             // update styles
43297             if (hasStyles) { 
43298                 var st = this.tb.fields.item(0);
43299                 
43300                 st.store.removeAll();
43301                
43302                 
43303                 var cn = sel.className.split(/\s+/);
43304                 
43305                 var avs = [];
43306                 if (this.styles['*']) {
43307                     
43308                     Roo.each(this.styles['*'], function(v) {
43309                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43310                     });
43311                 }
43312                 if (this.styles[tn]) { 
43313                     Roo.each(this.styles[tn], function(v) {
43314                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43315                     });
43316                 }
43317                 
43318                 st.store.loadData(avs);
43319                 st.collapse();
43320                 st.setValue(cn);
43321             }
43322             // flag our selected Node.
43323             this.tb.selectedNode = sel;
43324            
43325            
43326             Roo.menu.MenuMgr.hideAll();
43327
43328         }
43329         
43330         if (!updateFooter) {
43331             //this.footDisp.dom.innerHTML = ''; 
43332             return;
43333         }
43334         // update the footer
43335         //
43336         var html = '';
43337         
43338         this.footerEls = ans.reverse();
43339         Roo.each(this.footerEls, function(a,i) {
43340             if (!a) { return; }
43341             html += html.length ? ' &gt; '  :  '';
43342             
43343             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43344             
43345         });
43346        
43347         // 
43348         var sz = this.footDisp.up('td').getSize();
43349         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43350         this.footDisp.dom.style.marginLeft = '5px';
43351         
43352         this.footDisp.dom.style.overflow = 'hidden';
43353         
43354         this.footDisp.dom.innerHTML = html;
43355             
43356         //this.editorsyncValue();
43357     },
43358      
43359     
43360    
43361        
43362     // private
43363     onDestroy : function(){
43364         if(this.rendered){
43365             
43366             this.tb.items.each(function(item){
43367                 if(item.menu){
43368                     item.menu.removeAll();
43369                     if(item.menu.el){
43370                         item.menu.el.destroy();
43371                     }
43372                 }
43373                 item.destroy();
43374             });
43375              
43376         }
43377     },
43378     onFirstFocus: function() {
43379         // need to do this for all the toolbars..
43380         this.tb.items.each(function(item){
43381            item.enable();
43382         });
43383     },
43384     buildToolbar: function(tlist, nm)
43385     {
43386         var editor = this.editor;
43387          // create a new element.
43388         var wdiv = editor.wrap.createChild({
43389                 tag: 'div'
43390             }, editor.wrap.dom.firstChild.nextSibling, true);
43391         
43392        
43393         var tb = new Roo.Toolbar(wdiv);
43394         // add the name..
43395         
43396         tb.add(nm+ ":&nbsp;");
43397         
43398         var styles = [];
43399         for(var i in this.styles) {
43400             styles.push(i);
43401         }
43402         
43403         // styles...
43404         if (styles && styles.length) {
43405             
43406             // this needs a multi-select checkbox...
43407             tb.addField( new Roo.form.ComboBox({
43408                 store: new Roo.data.SimpleStore({
43409                     id : 'val',
43410                     fields: ['val', 'selected'],
43411                     data : [] 
43412                 }),
43413                 name : '-roo-edit-className',
43414                 attrname : 'className',
43415                 displayField: 'val',
43416                 typeAhead: false,
43417                 mode: 'local',
43418                 editable : false,
43419                 triggerAction: 'all',
43420                 emptyText:'Select Style',
43421                 selectOnFocus:true,
43422                 width: 130,
43423                 listeners : {
43424                     'select': function(c, r, i) {
43425                         // initial support only for on class per el..
43426                         tb.selectedNode.className =  r ? r.get('val') : '';
43427                         editor.syncValue();
43428                     }
43429                 }
43430     
43431             }));
43432         }
43433         
43434         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43435         var tbops = tbc.options;
43436         
43437         for (var i in tlist) {
43438             
43439             var item = tlist[i];
43440             tb.add(item.title + ":&nbsp;");
43441             
43442             
43443             //optname == used so you can configure the options available..
43444             var opts = item.opts ? item.opts : false;
43445             if (item.optname) {
43446                 opts = tbops[item.optname];
43447            
43448             }
43449             
43450             if (opts) {
43451                 // opts == pulldown..
43452                 tb.addField( new Roo.form.ComboBox({
43453                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43454                         id : 'val',
43455                         fields: ['val', 'display'],
43456                         data : opts  
43457                     }),
43458                     name : '-roo-edit-' + i,
43459                     attrname : i,
43460                     stylename : item.style ? item.style : false,
43461                     displayField: item.displayField ? item.displayField : 'val',
43462                     valueField :  'val',
43463                     typeAhead: false,
43464                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43465                     editable : false,
43466                     triggerAction: 'all',
43467                     emptyText:'Select',
43468                     selectOnFocus:true,
43469                     width: item.width ? item.width  : 130,
43470                     listeners : {
43471                         'select': function(c, r, i) {
43472                             if (c.stylename) {
43473                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43474                                 return;
43475                             }
43476                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43477                         }
43478                     }
43479
43480                 }));
43481                 continue;
43482                     
43483                  
43484                 
43485                 tb.addField( new Roo.form.TextField({
43486                     name: i,
43487                     width: 100,
43488                     //allowBlank:false,
43489                     value: ''
43490                 }));
43491                 continue;
43492             }
43493             tb.addField( new Roo.form.TextField({
43494                 name: '-roo-edit-' + i,
43495                 attrname : i,
43496                 
43497                 width: item.width,
43498                 //allowBlank:true,
43499                 value: '',
43500                 listeners: {
43501                     'change' : function(f, nv, ov) {
43502                         tb.selectedNode.setAttribute(f.attrname, nv);
43503                     }
43504                 }
43505             }));
43506              
43507         }
43508         tb.addFill();
43509         var _this = this;
43510         tb.addButton( {
43511             text: 'Remove Tag',
43512     
43513             listeners : {
43514                 click : function ()
43515                 {
43516                     // remove
43517                     // undo does not work.
43518                      
43519                     var sn = tb.selectedNode;
43520                     
43521                     var pn = sn.parentNode;
43522                     
43523                     var stn =  sn.childNodes[0];
43524                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43525                     while (sn.childNodes.length) {
43526                         var node = sn.childNodes[0];
43527                         sn.removeChild(node);
43528                         //Roo.log(node);
43529                         pn.insertBefore(node, sn);
43530                         
43531                     }
43532                     pn.removeChild(sn);
43533                     var range = editor.createRange();
43534         
43535                     range.setStart(stn,0);
43536                     range.setEnd(en,0); //????
43537                     //range.selectNode(sel);
43538                     
43539                     
43540                     var selection = editor.getSelection();
43541                     selection.removeAllRanges();
43542                     selection.addRange(range);
43543                     
43544                     
43545                     
43546                     //_this.updateToolbar(null, null, pn);
43547                     _this.updateToolbar(null, null, null);
43548                     _this.footDisp.dom.innerHTML = ''; 
43549                 }
43550             }
43551             
43552                     
43553                 
43554             
43555         });
43556         
43557         
43558         tb.el.on('click', function(e){
43559             e.preventDefault(); // what does this do?
43560         });
43561         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43562         tb.el.hide();
43563         tb.name = nm;
43564         // dont need to disable them... as they will get hidden
43565         return tb;
43566          
43567         
43568     },
43569     buildFooter : function()
43570     {
43571         
43572         var fel = this.editor.wrap.createChild();
43573         this.footer = new Roo.Toolbar(fel);
43574         // toolbar has scrolly on left / right?
43575         var footDisp= new Roo.Toolbar.Fill();
43576         var _t = this;
43577         this.footer.add(
43578             {
43579                 text : '&lt;',
43580                 xtype: 'Button',
43581                 handler : function() {
43582                     _t.footDisp.scrollTo('left',0,true)
43583                 }
43584             }
43585         );
43586         this.footer.add( footDisp );
43587         this.footer.add( 
43588             {
43589                 text : '&gt;',
43590                 xtype: 'Button',
43591                 handler : function() {
43592                     // no animation..
43593                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43594                 }
43595             }
43596         );
43597         var fel = Roo.get(footDisp.el);
43598         fel.addClass('x-editor-context');
43599         this.footDispWrap = fel; 
43600         this.footDispWrap.overflow  = 'hidden';
43601         
43602         this.footDisp = fel.createChild();
43603         this.footDispWrap.on('click', this.onContextClick, this)
43604         
43605         
43606     },
43607     onContextClick : function (ev,dom)
43608     {
43609         ev.preventDefault();
43610         var  cn = dom.className;
43611         //Roo.log(cn);
43612         if (!cn.match(/x-ed-loc-/)) {
43613             return;
43614         }
43615         var n = cn.split('-').pop();
43616         var ans = this.footerEls;
43617         var sel = ans[n];
43618         
43619          // pick
43620         var range = this.editor.createRange();
43621         
43622         range.selectNodeContents(sel);
43623         //range.selectNode(sel);
43624         
43625         
43626         var selection = this.editor.getSelection();
43627         selection.removeAllRanges();
43628         selection.addRange(range);
43629         
43630         
43631         
43632         this.updateToolbar(null, null, sel);
43633         
43634         
43635     }
43636     
43637     
43638     
43639     
43640     
43641 });
43642
43643
43644
43645
43646
43647 /*
43648  * Based on:
43649  * Ext JS Library 1.1.1
43650  * Copyright(c) 2006-2007, Ext JS, LLC.
43651  *
43652  * Originally Released Under LGPL - original licence link has changed is not relivant.
43653  *
43654  * Fork - LGPL
43655  * <script type="text/javascript">
43656  */
43657  
43658 /**
43659  * @class Roo.form.BasicForm
43660  * @extends Roo.util.Observable
43661  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43662  * @constructor
43663  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43664  * @param {Object} config Configuration options
43665  */
43666 Roo.form.BasicForm = function(el, config){
43667     this.allItems = [];
43668     this.childForms = [];
43669     Roo.apply(this, config);
43670     /*
43671      * The Roo.form.Field items in this form.
43672      * @type MixedCollection
43673      */
43674      
43675      
43676     this.items = new Roo.util.MixedCollection(false, function(o){
43677         return o.id || (o.id = Roo.id());
43678     });
43679     this.addEvents({
43680         /**
43681          * @event beforeaction
43682          * Fires before any action is performed. Return false to cancel the action.
43683          * @param {Form} this
43684          * @param {Action} action The action to be performed
43685          */
43686         beforeaction: true,
43687         /**
43688          * @event actionfailed
43689          * Fires when an action fails.
43690          * @param {Form} this
43691          * @param {Action} action The action that failed
43692          */
43693         actionfailed : true,
43694         /**
43695          * @event actioncomplete
43696          * Fires when an action is completed.
43697          * @param {Form} this
43698          * @param {Action} action The action that completed
43699          */
43700         actioncomplete : true
43701     });
43702     if(el){
43703         this.initEl(el);
43704     }
43705     Roo.form.BasicForm.superclass.constructor.call(this);
43706 };
43707
43708 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43709     /**
43710      * @cfg {String} method
43711      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43712      */
43713     /**
43714      * @cfg {DataReader} reader
43715      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43716      * This is optional as there is built-in support for processing JSON.
43717      */
43718     /**
43719      * @cfg {DataReader} errorReader
43720      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43721      * This is completely optional as there is built-in support for processing JSON.
43722      */
43723     /**
43724      * @cfg {String} url
43725      * The URL to use for form actions if one isn't supplied in the action options.
43726      */
43727     /**
43728      * @cfg {Boolean} fileUpload
43729      * Set to true if this form is a file upload.
43730      */
43731      
43732     /**
43733      * @cfg {Object} baseParams
43734      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43735      */
43736      /**
43737      
43738     /**
43739      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43740      */
43741     timeout: 30,
43742
43743     // private
43744     activeAction : null,
43745
43746     /**
43747      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43748      * or setValues() data instead of when the form was first created.
43749      */
43750     trackResetOnLoad : false,
43751     
43752     
43753     /**
43754      * childForms - used for multi-tab forms
43755      * @type {Array}
43756      */
43757     childForms : false,
43758     
43759     /**
43760      * allItems - full list of fields.
43761      * @type {Array}
43762      */
43763     allItems : false,
43764     
43765     /**
43766      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43767      * element by passing it or its id or mask the form itself by passing in true.
43768      * @type Mixed
43769      */
43770     waitMsgTarget : false,
43771
43772     // private
43773     initEl : function(el){
43774         this.el = Roo.get(el);
43775         this.id = this.el.id || Roo.id();
43776         this.el.on('submit', this.onSubmit, this);
43777         this.el.addClass('x-form');
43778     },
43779
43780     // private
43781     onSubmit : function(e){
43782         e.stopEvent();
43783     },
43784
43785     /**
43786      * Returns true if client-side validation on the form is successful.
43787      * @return Boolean
43788      */
43789     isValid : function(){
43790         var valid = true;
43791         this.items.each(function(f){
43792            if(!f.validate()){
43793                valid = false;
43794            }
43795         });
43796         return valid;
43797     },
43798
43799     /**
43800      * Returns true if any fields in this form have changed since their original load.
43801      * @return Boolean
43802      */
43803     isDirty : function(){
43804         var dirty = false;
43805         this.items.each(function(f){
43806            if(f.isDirty()){
43807                dirty = true;
43808                return false;
43809            }
43810         });
43811         return dirty;
43812     },
43813
43814     /**
43815      * Performs a predefined action (submit or load) or custom actions you define on this form.
43816      * @param {String} actionName The name of the action type
43817      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43818      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43819      * accept other config options):
43820      * <pre>
43821 Property          Type             Description
43822 ----------------  ---------------  ----------------------------------------------------------------------------------
43823 url               String           The url for the action (defaults to the form's url)
43824 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43825 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43826 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43827                                    validate the form on the client (defaults to false)
43828      * </pre>
43829      * @return {BasicForm} this
43830      */
43831     doAction : function(action, options){
43832         if(typeof action == 'string'){
43833             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43834         }
43835         if(this.fireEvent('beforeaction', this, action) !== false){
43836             this.beforeAction(action);
43837             action.run.defer(100, action);
43838         }
43839         return this;
43840     },
43841
43842     /**
43843      * Shortcut to do a submit action.
43844      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43845      * @return {BasicForm} this
43846      */
43847     submit : function(options){
43848         this.doAction('submit', options);
43849         return this;
43850     },
43851
43852     /**
43853      * Shortcut to do a load action.
43854      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43855      * @return {BasicForm} this
43856      */
43857     load : function(options){
43858         this.doAction('load', options);
43859         return this;
43860     },
43861
43862     /**
43863      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43864      * @param {Record} record The record to edit
43865      * @return {BasicForm} this
43866      */
43867     updateRecord : function(record){
43868         record.beginEdit();
43869         var fs = record.fields;
43870         fs.each(function(f){
43871             var field = this.findField(f.name);
43872             if(field){
43873                 record.set(f.name, field.getValue());
43874             }
43875         }, this);
43876         record.endEdit();
43877         return this;
43878     },
43879
43880     /**
43881      * Loads an Roo.data.Record into this form.
43882      * @param {Record} record The record to load
43883      * @return {BasicForm} this
43884      */
43885     loadRecord : function(record){
43886         this.setValues(record.data);
43887         return this;
43888     },
43889
43890     // private
43891     beforeAction : function(action){
43892         var o = action.options;
43893         
43894        
43895         if(this.waitMsgTarget === true){
43896             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43897         }else if(this.waitMsgTarget){
43898             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43899             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43900         }else {
43901             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43902         }
43903          
43904     },
43905
43906     // private
43907     afterAction : function(action, success){
43908         this.activeAction = null;
43909         var o = action.options;
43910         
43911         if(this.waitMsgTarget === true){
43912             this.el.unmask();
43913         }else if(this.waitMsgTarget){
43914             this.waitMsgTarget.unmask();
43915         }else{
43916             Roo.MessageBox.updateProgress(1);
43917             Roo.MessageBox.hide();
43918         }
43919          
43920         if(success){
43921             if(o.reset){
43922                 this.reset();
43923             }
43924             Roo.callback(o.success, o.scope, [this, action]);
43925             this.fireEvent('actioncomplete', this, action);
43926             
43927         }else{
43928             
43929             // failure condition..
43930             // we have a scenario where updates need confirming.
43931             // eg. if a locking scenario exists..
43932             // we look for { errors : { needs_confirm : true }} in the response.
43933             if (
43934                 (typeof(action.result) != 'undefined')  &&
43935                 (typeof(action.result.errors) != 'undefined')  &&
43936                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43937            ){
43938                 var _t = this;
43939                 Roo.MessageBox.confirm(
43940                     "Change requires confirmation",
43941                     action.result.errorMsg,
43942                     function(r) {
43943                         if (r != 'yes') {
43944                             return;
43945                         }
43946                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43947                     }
43948                     
43949                 );
43950                 
43951                 
43952                 
43953                 return;
43954             }
43955             
43956             Roo.callback(o.failure, o.scope, [this, action]);
43957             // show an error message if no failed handler is set..
43958             if (!this.hasListener('actionfailed')) {
43959                 Roo.MessageBox.alert("Error",
43960                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43961                         action.result.errorMsg :
43962                         "Saving Failed, please check your entries or try again"
43963                 );
43964             }
43965             
43966             this.fireEvent('actionfailed', this, action);
43967         }
43968         
43969     },
43970
43971     /**
43972      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43973      * @param {String} id The value to search for
43974      * @return Field
43975      */
43976     findField : function(id){
43977         var field = this.items.get(id);
43978         if(!field){
43979             this.items.each(function(f){
43980                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43981                     field = f;
43982                     return false;
43983                 }
43984             });
43985         }
43986         return field || null;
43987     },
43988
43989     /**
43990      * Add a secondary form to this one, 
43991      * Used to provide tabbed forms. One form is primary, with hidden values 
43992      * which mirror the elements from the other forms.
43993      * 
43994      * @param {Roo.form.Form} form to add.
43995      * 
43996      */
43997     addForm : function(form)
43998     {
43999        
44000         if (this.childForms.indexOf(form) > -1) {
44001             // already added..
44002             return;
44003         }
44004         this.childForms.push(form);
44005         var n = '';
44006         Roo.each(form.allItems, function (fe) {
44007             
44008             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44009             if (this.findField(n)) { // already added..
44010                 return;
44011             }
44012             var add = new Roo.form.Hidden({
44013                 name : n
44014             });
44015             add.render(this.el);
44016             
44017             this.add( add );
44018         }, this);
44019         
44020     },
44021     /**
44022      * Mark fields in this form invalid in bulk.
44023      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44024      * @return {BasicForm} this
44025      */
44026     markInvalid : function(errors){
44027         if(errors instanceof Array){
44028             for(var i = 0, len = errors.length; i < len; i++){
44029                 var fieldError = errors[i];
44030                 var f = this.findField(fieldError.id);
44031                 if(f){
44032                     f.markInvalid(fieldError.msg);
44033                 }
44034             }
44035         }else{
44036             var field, id;
44037             for(id in errors){
44038                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44039                     field.markInvalid(errors[id]);
44040                 }
44041             }
44042         }
44043         Roo.each(this.childForms || [], function (f) {
44044             f.markInvalid(errors);
44045         });
44046         
44047         return this;
44048     },
44049
44050     /**
44051      * Set values for fields in this form in bulk.
44052      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44053      * @return {BasicForm} this
44054      */
44055     setValues : function(values){
44056         if(values instanceof Array){ // array of objects
44057             for(var i = 0, len = values.length; i < len; i++){
44058                 var v = values[i];
44059                 var f = this.findField(v.id);
44060                 if(f){
44061                     f.setValue(v.value);
44062                     if(this.trackResetOnLoad){
44063                         f.originalValue = f.getValue();
44064                     }
44065                 }
44066             }
44067         }else{ // object hash
44068             var field, id;
44069             for(id in values){
44070                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44071                     
44072                     if (field.setFromData && 
44073                         field.valueField && 
44074                         field.displayField &&
44075                         // combos' with local stores can 
44076                         // be queried via setValue()
44077                         // to set their value..
44078                         (field.store && !field.store.isLocal)
44079                         ) {
44080                         // it's a combo
44081                         var sd = { };
44082                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44083                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44084                         field.setFromData(sd);
44085                         
44086                     } else {
44087                         field.setValue(values[id]);
44088                     }
44089                     
44090                     
44091                     if(this.trackResetOnLoad){
44092                         field.originalValue = field.getValue();
44093                     }
44094                 }
44095             }
44096         }
44097          
44098         Roo.each(this.childForms || [], function (f) {
44099             f.setValues(values);
44100         });
44101                 
44102         return this;
44103     },
44104
44105     /**
44106      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44107      * they are returned as an array.
44108      * @param {Boolean} asString
44109      * @return {Object}
44110      */
44111     getValues : function(asString){
44112         if (this.childForms) {
44113             // copy values from the child forms
44114             Roo.each(this.childForms, function (f) {
44115                 this.setValues(f.getValues());
44116             }, this);
44117         }
44118         
44119         
44120         
44121         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44122         if(asString === true){
44123             return fs;
44124         }
44125         return Roo.urlDecode(fs);
44126     },
44127     
44128     /**
44129      * Returns the fields in this form as an object with key/value pairs. 
44130      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44131      * @return {Object}
44132      */
44133     getFieldValues : function(with_hidden)
44134     {
44135         if (this.childForms) {
44136             // copy values from the child forms
44137             // should this call getFieldValues - probably not as we do not currently copy
44138             // hidden fields when we generate..
44139             Roo.each(this.childForms, function (f) {
44140                 this.setValues(f.getValues());
44141             }, this);
44142         }
44143         
44144         var ret = {};
44145         this.items.each(function(f){
44146             if (!f.getName()) {
44147                 return;
44148             }
44149             var v = f.getValue();
44150             if (f.inputType =='radio') {
44151                 if (typeof(ret[f.getName()]) == 'undefined') {
44152                     ret[f.getName()] = ''; // empty..
44153                 }
44154                 
44155                 if (!f.el.dom.checked) {
44156                     return;
44157                     
44158                 }
44159                 v = f.el.dom.value;
44160                 
44161             }
44162             
44163             // not sure if this supported any more..
44164             if ((typeof(v) == 'object') && f.getRawValue) {
44165                 v = f.getRawValue() ; // dates..
44166             }
44167             // combo boxes where name != hiddenName...
44168             if (f.name != f.getName()) {
44169                 ret[f.name] = f.getRawValue();
44170             }
44171             ret[f.getName()] = v;
44172         });
44173         
44174         return ret;
44175     },
44176
44177     /**
44178      * Clears all invalid messages in this form.
44179      * @return {BasicForm} this
44180      */
44181     clearInvalid : function(){
44182         this.items.each(function(f){
44183            f.clearInvalid();
44184         });
44185         
44186         Roo.each(this.childForms || [], function (f) {
44187             f.clearInvalid();
44188         });
44189         
44190         
44191         return this;
44192     },
44193
44194     /**
44195      * Resets this form.
44196      * @return {BasicForm} this
44197      */
44198     reset : function(){
44199         this.items.each(function(f){
44200             f.reset();
44201         });
44202         
44203         Roo.each(this.childForms || [], function (f) {
44204             f.reset();
44205         });
44206        
44207         
44208         return this;
44209     },
44210
44211     /**
44212      * Add Roo.form components to this form.
44213      * @param {Field} field1
44214      * @param {Field} field2 (optional)
44215      * @param {Field} etc (optional)
44216      * @return {BasicForm} this
44217      */
44218     add : function(){
44219         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44220         return this;
44221     },
44222
44223
44224     /**
44225      * Removes a field from the items collection (does NOT remove its markup).
44226      * @param {Field} field
44227      * @return {BasicForm} this
44228      */
44229     remove : function(field){
44230         this.items.remove(field);
44231         return this;
44232     },
44233
44234     /**
44235      * Looks at the fields in this form, checks them for an id attribute,
44236      * and calls applyTo on the existing dom element with that id.
44237      * @return {BasicForm} this
44238      */
44239     render : function(){
44240         this.items.each(function(f){
44241             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44242                 f.applyTo(f.id);
44243             }
44244         });
44245         return this;
44246     },
44247
44248     /**
44249      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44250      * @param {Object} values
44251      * @return {BasicForm} this
44252      */
44253     applyToFields : function(o){
44254         this.items.each(function(f){
44255            Roo.apply(f, o);
44256         });
44257         return this;
44258     },
44259
44260     /**
44261      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44262      * @param {Object} values
44263      * @return {BasicForm} this
44264      */
44265     applyIfToFields : function(o){
44266         this.items.each(function(f){
44267            Roo.applyIf(f, o);
44268         });
44269         return this;
44270     }
44271 });
44272
44273 // back compat
44274 Roo.BasicForm = Roo.form.BasicForm;/*
44275  * Based on:
44276  * Ext JS Library 1.1.1
44277  * Copyright(c) 2006-2007, Ext JS, LLC.
44278  *
44279  * Originally Released Under LGPL - original licence link has changed is not relivant.
44280  *
44281  * Fork - LGPL
44282  * <script type="text/javascript">
44283  */
44284
44285 /**
44286  * @class Roo.form.Form
44287  * @extends Roo.form.BasicForm
44288  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44289  * @constructor
44290  * @param {Object} config Configuration options
44291  */
44292 Roo.form.Form = function(config){
44293     var xitems =  [];
44294     if (config.items) {
44295         xitems = config.items;
44296         delete config.items;
44297     }
44298    
44299     
44300     Roo.form.Form.superclass.constructor.call(this, null, config);
44301     this.url = this.url || this.action;
44302     if(!this.root){
44303         this.root = new Roo.form.Layout(Roo.applyIf({
44304             id: Roo.id()
44305         }, config));
44306     }
44307     this.active = this.root;
44308     /**
44309      * Array of all the buttons that have been added to this form via {@link addButton}
44310      * @type Array
44311      */
44312     this.buttons = [];
44313     this.allItems = [];
44314     this.addEvents({
44315         /**
44316          * @event clientvalidation
44317          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44318          * @param {Form} this
44319          * @param {Boolean} valid true if the form has passed client-side validation
44320          */
44321         clientvalidation: true,
44322         /**
44323          * @event rendered
44324          * Fires when the form is rendered
44325          * @param {Roo.form.Form} form
44326          */
44327         rendered : true
44328     });
44329     
44330     if (this.progressUrl) {
44331             // push a hidden field onto the list of fields..
44332             this.addxtype( {
44333                     xns: Roo.form, 
44334                     xtype : 'Hidden', 
44335                     name : 'UPLOAD_IDENTIFIER' 
44336             });
44337         }
44338         
44339     
44340     Roo.each(xitems, this.addxtype, this);
44341     
44342     
44343     
44344 };
44345
44346 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44347     /**
44348      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44349      */
44350     /**
44351      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44352      */
44353     /**
44354      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44355      */
44356     buttonAlign:'center',
44357
44358     /**
44359      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44360      */
44361     minButtonWidth:75,
44362
44363     /**
44364      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44365      * This property cascades to child containers if not set.
44366      */
44367     labelAlign:'left',
44368
44369     /**
44370      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44371      * fires a looping event with that state. This is required to bind buttons to the valid
44372      * state using the config value formBind:true on the button.
44373      */
44374     monitorValid : false,
44375
44376     /**
44377      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44378      */
44379     monitorPoll : 200,
44380     
44381     /**
44382      * @cfg {String} progressUrl - Url to return progress data 
44383      */
44384     
44385     progressUrl : false,
44386   
44387     /**
44388      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44389      * fields are added and the column is closed. If no fields are passed the column remains open
44390      * until end() is called.
44391      * @param {Object} config The config to pass to the column
44392      * @param {Field} field1 (optional)
44393      * @param {Field} field2 (optional)
44394      * @param {Field} etc (optional)
44395      * @return Column The column container object
44396      */
44397     column : function(c){
44398         var col = new Roo.form.Column(c);
44399         this.start(col);
44400         if(arguments.length > 1){ // duplicate code required because of Opera
44401             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44402             this.end();
44403         }
44404         return col;
44405     },
44406
44407     /**
44408      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44409      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44410      * until end() is called.
44411      * @param {Object} config The config to pass to the fieldset
44412      * @param {Field} field1 (optional)
44413      * @param {Field} field2 (optional)
44414      * @param {Field} etc (optional)
44415      * @return FieldSet The fieldset container object
44416      */
44417     fieldset : function(c){
44418         var fs = new Roo.form.FieldSet(c);
44419         this.start(fs);
44420         if(arguments.length > 1){ // duplicate code required because of Opera
44421             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44422             this.end();
44423         }
44424         return fs;
44425     },
44426
44427     /**
44428      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44429      * fields are added and the container is closed. If no fields are passed the container remains open
44430      * until end() is called.
44431      * @param {Object} config The config to pass to the Layout
44432      * @param {Field} field1 (optional)
44433      * @param {Field} field2 (optional)
44434      * @param {Field} etc (optional)
44435      * @return Layout The container object
44436      */
44437     container : function(c){
44438         var l = new Roo.form.Layout(c);
44439         this.start(l);
44440         if(arguments.length > 1){ // duplicate code required because of Opera
44441             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44442             this.end();
44443         }
44444         return l;
44445     },
44446
44447     /**
44448      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44449      * @param {Object} container A Roo.form.Layout or subclass of Layout
44450      * @return {Form} this
44451      */
44452     start : function(c){
44453         // cascade label info
44454         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44455         this.active.stack.push(c);
44456         c.ownerCt = this.active;
44457         this.active = c;
44458         return this;
44459     },
44460
44461     /**
44462      * Closes the current open container
44463      * @return {Form} this
44464      */
44465     end : function(){
44466         if(this.active == this.root){
44467             return this;
44468         }
44469         this.active = this.active.ownerCt;
44470         return this;
44471     },
44472
44473     /**
44474      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44475      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44476      * as the label of the field.
44477      * @param {Field} field1
44478      * @param {Field} field2 (optional)
44479      * @param {Field} etc. (optional)
44480      * @return {Form} this
44481      */
44482     add : function(){
44483         this.active.stack.push.apply(this.active.stack, arguments);
44484         this.allItems.push.apply(this.allItems,arguments);
44485         var r = [];
44486         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44487             if(a[i].isFormField){
44488                 r.push(a[i]);
44489             }
44490         }
44491         if(r.length > 0){
44492             Roo.form.Form.superclass.add.apply(this, r);
44493         }
44494         return this;
44495     },
44496     
44497
44498     
44499     
44500     
44501      /**
44502      * Find any element that has been added to a form, using it's ID or name
44503      * This can include framesets, columns etc. along with regular fields..
44504      * @param {String} id - id or name to find.
44505      
44506      * @return {Element} e - or false if nothing found.
44507      */
44508     findbyId : function(id)
44509     {
44510         var ret = false;
44511         if (!id) {
44512             return ret;
44513         }
44514         Roo.each(this.allItems, function(f){
44515             if (f.id == id || f.name == id ){
44516                 ret = f;
44517                 return false;
44518             }
44519         });
44520         return ret;
44521     },
44522
44523     
44524     
44525     /**
44526      * Render this form into the passed container. This should only be called once!
44527      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44528      * @return {Form} this
44529      */
44530     render : function(ct)
44531     {
44532         
44533         
44534         
44535         ct = Roo.get(ct);
44536         var o = this.autoCreate || {
44537             tag: 'form',
44538             method : this.method || 'POST',
44539             id : this.id || Roo.id()
44540         };
44541         this.initEl(ct.createChild(o));
44542
44543         this.root.render(this.el);
44544         
44545        
44546              
44547         this.items.each(function(f){
44548             f.render('x-form-el-'+f.id);
44549         });
44550
44551         if(this.buttons.length > 0){
44552             // tables are required to maintain order and for correct IE layout
44553             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44554                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44555                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44556             }}, null, true);
44557             var tr = tb.getElementsByTagName('tr')[0];
44558             for(var i = 0, len = this.buttons.length; i < len; i++) {
44559                 var b = this.buttons[i];
44560                 var td = document.createElement('td');
44561                 td.className = 'x-form-btn-td';
44562                 b.render(tr.appendChild(td));
44563             }
44564         }
44565         if(this.monitorValid){ // initialize after render
44566             this.startMonitoring();
44567         }
44568         this.fireEvent('rendered', this);
44569         return this;
44570     },
44571
44572     /**
44573      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44574      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44575      * object or a valid Roo.DomHelper element config
44576      * @param {Function} handler The function called when the button is clicked
44577      * @param {Object} scope (optional) The scope of the handler function
44578      * @return {Roo.Button}
44579      */
44580     addButton : function(config, handler, scope){
44581         var bc = {
44582             handler: handler,
44583             scope: scope,
44584             minWidth: this.minButtonWidth,
44585             hideParent:true
44586         };
44587         if(typeof config == "string"){
44588             bc.text = config;
44589         }else{
44590             Roo.apply(bc, config);
44591         }
44592         var btn = new Roo.Button(null, bc);
44593         this.buttons.push(btn);
44594         return btn;
44595     },
44596
44597      /**
44598      * Adds a series of form elements (using the xtype property as the factory method.
44599      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44600      * @param {Object} config 
44601      */
44602     
44603     addxtype : function()
44604     {
44605         var ar = Array.prototype.slice.call(arguments, 0);
44606         var ret = false;
44607         for(var i = 0; i < ar.length; i++) {
44608             if (!ar[i]) {
44609                 continue; // skip -- if this happends something invalid got sent, we 
44610                 // should ignore it, as basically that interface element will not show up
44611                 // and that should be pretty obvious!!
44612             }
44613             
44614             if (Roo.form[ar[i].xtype]) {
44615                 ar[i].form = this;
44616                 var fe = Roo.factory(ar[i], Roo.form);
44617                 if (!ret) {
44618                     ret = fe;
44619                 }
44620                 fe.form = this;
44621                 if (fe.store) {
44622                     fe.store.form = this;
44623                 }
44624                 if (fe.isLayout) {  
44625                          
44626                     this.start(fe);
44627                     this.allItems.push(fe);
44628                     if (fe.items && fe.addxtype) {
44629                         fe.addxtype.apply(fe, fe.items);
44630                         delete fe.items;
44631                     }
44632                      this.end();
44633                     continue;
44634                 }
44635                 
44636                 
44637                  
44638                 this.add(fe);
44639               //  console.log('adding ' + ar[i].xtype);
44640             }
44641             if (ar[i].xtype == 'Button') {  
44642                 //console.log('adding button');
44643                 //console.log(ar[i]);
44644                 this.addButton(ar[i]);
44645                 this.allItems.push(fe);
44646                 continue;
44647             }
44648             
44649             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44650                 alert('end is not supported on xtype any more, use items');
44651             //    this.end();
44652             //    //console.log('adding end');
44653             }
44654             
44655         }
44656         return ret;
44657     },
44658     
44659     /**
44660      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44661      * option "monitorValid"
44662      */
44663     startMonitoring : function(){
44664         if(!this.bound){
44665             this.bound = true;
44666             Roo.TaskMgr.start({
44667                 run : this.bindHandler,
44668                 interval : this.monitorPoll || 200,
44669                 scope: this
44670             });
44671         }
44672     },
44673
44674     /**
44675      * Stops monitoring of the valid state of this form
44676      */
44677     stopMonitoring : function(){
44678         this.bound = false;
44679     },
44680
44681     // private
44682     bindHandler : function(){
44683         if(!this.bound){
44684             return false; // stops binding
44685         }
44686         var valid = true;
44687         this.items.each(function(f){
44688             if(!f.isValid(true)){
44689                 valid = false;
44690                 return false;
44691             }
44692         });
44693         for(var i = 0, len = this.buttons.length; i < len; i++){
44694             var btn = this.buttons[i];
44695             if(btn.formBind === true && btn.disabled === valid){
44696                 btn.setDisabled(!valid);
44697             }
44698         }
44699         this.fireEvent('clientvalidation', this, valid);
44700     }
44701     
44702     
44703     
44704     
44705     
44706     
44707     
44708     
44709 });
44710
44711
44712 // back compat
44713 Roo.Form = Roo.form.Form;
44714 /*
44715  * Based on:
44716  * Ext JS Library 1.1.1
44717  * Copyright(c) 2006-2007, Ext JS, LLC.
44718  *
44719  * Originally Released Under LGPL - original licence link has changed is not relivant.
44720  *
44721  * Fork - LGPL
44722  * <script type="text/javascript">
44723  */
44724
44725 // as we use this in bootstrap.
44726 Roo.namespace('Roo.form');
44727  /**
44728  * @class Roo.form.Action
44729  * Internal Class used to handle form actions
44730  * @constructor
44731  * @param {Roo.form.BasicForm} el The form element or its id
44732  * @param {Object} config Configuration options
44733  */
44734
44735  
44736  
44737 // define the action interface
44738 Roo.form.Action = function(form, options){
44739     this.form = form;
44740     this.options = options || {};
44741 };
44742 /**
44743  * Client Validation Failed
44744  * @const 
44745  */
44746 Roo.form.Action.CLIENT_INVALID = 'client';
44747 /**
44748  * Server Validation Failed
44749  * @const 
44750  */
44751 Roo.form.Action.SERVER_INVALID = 'server';
44752  /**
44753  * Connect to Server Failed
44754  * @const 
44755  */
44756 Roo.form.Action.CONNECT_FAILURE = 'connect';
44757 /**
44758  * Reading Data from Server Failed
44759  * @const 
44760  */
44761 Roo.form.Action.LOAD_FAILURE = 'load';
44762
44763 Roo.form.Action.prototype = {
44764     type : 'default',
44765     failureType : undefined,
44766     response : undefined,
44767     result : undefined,
44768
44769     // interface method
44770     run : function(options){
44771
44772     },
44773
44774     // interface method
44775     success : function(response){
44776
44777     },
44778
44779     // interface method
44780     handleResponse : function(response){
44781
44782     },
44783
44784     // default connection failure
44785     failure : function(response){
44786         
44787         this.response = response;
44788         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44789         this.form.afterAction(this, false);
44790     },
44791
44792     processResponse : function(response){
44793         this.response = response;
44794         if(!response.responseText){
44795             return true;
44796         }
44797         this.result = this.handleResponse(response);
44798         return this.result;
44799     },
44800
44801     // utility functions used internally
44802     getUrl : function(appendParams){
44803         var url = this.options.url || this.form.url || this.form.el.dom.action;
44804         if(appendParams){
44805             var p = this.getParams();
44806             if(p){
44807                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44808             }
44809         }
44810         return url;
44811     },
44812
44813     getMethod : function(){
44814         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44815     },
44816
44817     getParams : function(){
44818         var bp = this.form.baseParams;
44819         var p = this.options.params;
44820         if(p){
44821             if(typeof p == "object"){
44822                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44823             }else if(typeof p == 'string' && bp){
44824                 p += '&' + Roo.urlEncode(bp);
44825             }
44826         }else if(bp){
44827             p = Roo.urlEncode(bp);
44828         }
44829         return p;
44830     },
44831
44832     createCallback : function(){
44833         return {
44834             success: this.success,
44835             failure: this.failure,
44836             scope: this,
44837             timeout: (this.form.timeout*1000),
44838             upload: this.form.fileUpload ? this.success : undefined
44839         };
44840     }
44841 };
44842
44843 Roo.form.Action.Submit = function(form, options){
44844     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44845 };
44846
44847 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44848     type : 'submit',
44849
44850     haveProgress : false,
44851     uploadComplete : false,
44852     
44853     // uploadProgress indicator.
44854     uploadProgress : function()
44855     {
44856         if (!this.form.progressUrl) {
44857             return;
44858         }
44859         
44860         if (!this.haveProgress) {
44861             Roo.MessageBox.progress("Uploading", "Uploading");
44862         }
44863         if (this.uploadComplete) {
44864            Roo.MessageBox.hide();
44865            return;
44866         }
44867         
44868         this.haveProgress = true;
44869    
44870         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44871         
44872         var c = new Roo.data.Connection();
44873         c.request({
44874             url : this.form.progressUrl,
44875             params: {
44876                 id : uid
44877             },
44878             method: 'GET',
44879             success : function(req){
44880                //console.log(data);
44881                 var rdata = false;
44882                 var edata;
44883                 try  {
44884                    rdata = Roo.decode(req.responseText)
44885                 } catch (e) {
44886                     Roo.log("Invalid data from server..");
44887                     Roo.log(edata);
44888                     return;
44889                 }
44890                 if (!rdata || !rdata.success) {
44891                     Roo.log(rdata);
44892                     Roo.MessageBox.alert(Roo.encode(rdata));
44893                     return;
44894                 }
44895                 var data = rdata.data;
44896                 
44897                 if (this.uploadComplete) {
44898                    Roo.MessageBox.hide();
44899                    return;
44900                 }
44901                    
44902                 if (data){
44903                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44904                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44905                     );
44906                 }
44907                 this.uploadProgress.defer(2000,this);
44908             },
44909        
44910             failure: function(data) {
44911                 Roo.log('progress url failed ');
44912                 Roo.log(data);
44913             },
44914             scope : this
44915         });
44916            
44917     },
44918     
44919     
44920     run : function()
44921     {
44922         // run get Values on the form, so it syncs any secondary forms.
44923         this.form.getValues();
44924         
44925         var o = this.options;
44926         var method = this.getMethod();
44927         var isPost = method == 'POST';
44928         if(o.clientValidation === false || this.form.isValid()){
44929             
44930             if (this.form.progressUrl) {
44931                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44932                     (new Date() * 1) + '' + Math.random());
44933                     
44934             } 
44935             
44936             
44937             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44938                 form:this.form.el.dom,
44939                 url:this.getUrl(!isPost),
44940                 method: method,
44941                 params:isPost ? this.getParams() : null,
44942                 isUpload: this.form.fileUpload
44943             }));
44944             
44945             this.uploadProgress();
44946
44947         }else if (o.clientValidation !== false){ // client validation failed
44948             this.failureType = Roo.form.Action.CLIENT_INVALID;
44949             this.form.afterAction(this, false);
44950         }
44951     },
44952
44953     success : function(response)
44954     {
44955         this.uploadComplete= true;
44956         if (this.haveProgress) {
44957             Roo.MessageBox.hide();
44958         }
44959         
44960         
44961         var result = this.processResponse(response);
44962         if(result === true || result.success){
44963             this.form.afterAction(this, true);
44964             return;
44965         }
44966         if(result.errors){
44967             this.form.markInvalid(result.errors);
44968             this.failureType = Roo.form.Action.SERVER_INVALID;
44969         }
44970         this.form.afterAction(this, false);
44971     },
44972     failure : function(response)
44973     {
44974         this.uploadComplete= true;
44975         if (this.haveProgress) {
44976             Roo.MessageBox.hide();
44977         }
44978         
44979         this.response = response;
44980         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44981         this.form.afterAction(this, false);
44982     },
44983     
44984     handleResponse : function(response){
44985         if(this.form.errorReader){
44986             var rs = this.form.errorReader.read(response);
44987             var errors = [];
44988             if(rs.records){
44989                 for(var i = 0, len = rs.records.length; i < len; i++) {
44990                     var r = rs.records[i];
44991                     errors[i] = r.data;
44992                 }
44993             }
44994             if(errors.length < 1){
44995                 errors = null;
44996             }
44997             return {
44998                 success : rs.success,
44999                 errors : errors
45000             };
45001         }
45002         var ret = false;
45003         try {
45004             ret = Roo.decode(response.responseText);
45005         } catch (e) {
45006             ret = {
45007                 success: false,
45008                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45009                 errors : []
45010             };
45011         }
45012         return ret;
45013         
45014     }
45015 });
45016
45017
45018 Roo.form.Action.Load = function(form, options){
45019     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45020     this.reader = this.form.reader;
45021 };
45022
45023 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45024     type : 'load',
45025
45026     run : function(){
45027         
45028         Roo.Ajax.request(Roo.apply(
45029                 this.createCallback(), {
45030                     method:this.getMethod(),
45031                     url:this.getUrl(false),
45032                     params:this.getParams()
45033         }));
45034     },
45035
45036     success : function(response){
45037         
45038         var result = this.processResponse(response);
45039         if(result === true || !result.success || !result.data){
45040             this.failureType = Roo.form.Action.LOAD_FAILURE;
45041             this.form.afterAction(this, false);
45042             return;
45043         }
45044         this.form.clearInvalid();
45045         this.form.setValues(result.data);
45046         this.form.afterAction(this, true);
45047     },
45048
45049     handleResponse : function(response){
45050         if(this.form.reader){
45051             var rs = this.form.reader.read(response);
45052             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45053             return {
45054                 success : rs.success,
45055                 data : data
45056             };
45057         }
45058         return Roo.decode(response.responseText);
45059     }
45060 });
45061
45062 Roo.form.Action.ACTION_TYPES = {
45063     'load' : Roo.form.Action.Load,
45064     'submit' : Roo.form.Action.Submit
45065 };/*
45066  * Based on:
45067  * Ext JS Library 1.1.1
45068  * Copyright(c) 2006-2007, Ext JS, LLC.
45069  *
45070  * Originally Released Under LGPL - original licence link has changed is not relivant.
45071  *
45072  * Fork - LGPL
45073  * <script type="text/javascript">
45074  */
45075  
45076 /**
45077  * @class Roo.form.Layout
45078  * @extends Roo.Component
45079  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45080  * @constructor
45081  * @param {Object} config Configuration options
45082  */
45083 Roo.form.Layout = function(config){
45084     var xitems = [];
45085     if (config.items) {
45086         xitems = config.items;
45087         delete config.items;
45088     }
45089     Roo.form.Layout.superclass.constructor.call(this, config);
45090     this.stack = [];
45091     Roo.each(xitems, this.addxtype, this);
45092      
45093 };
45094
45095 Roo.extend(Roo.form.Layout, Roo.Component, {
45096     /**
45097      * @cfg {String/Object} autoCreate
45098      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45099      */
45100     /**
45101      * @cfg {String/Object/Function} style
45102      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45103      * a function which returns such a specification.
45104      */
45105     /**
45106      * @cfg {String} labelAlign
45107      * Valid values are "left," "top" and "right" (defaults to "left")
45108      */
45109     /**
45110      * @cfg {Number} labelWidth
45111      * Fixed width in pixels of all field labels (defaults to undefined)
45112      */
45113     /**
45114      * @cfg {Boolean} clear
45115      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45116      */
45117     clear : true,
45118     /**
45119      * @cfg {String} labelSeparator
45120      * The separator to use after field labels (defaults to ':')
45121      */
45122     labelSeparator : ':',
45123     /**
45124      * @cfg {Boolean} hideLabels
45125      * True to suppress the display of field labels in this layout (defaults to false)
45126      */
45127     hideLabels : false,
45128
45129     // private
45130     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45131     
45132     isLayout : true,
45133     
45134     // private
45135     onRender : function(ct, position){
45136         if(this.el){ // from markup
45137             this.el = Roo.get(this.el);
45138         }else {  // generate
45139             var cfg = this.getAutoCreate();
45140             this.el = ct.createChild(cfg, position);
45141         }
45142         if(this.style){
45143             this.el.applyStyles(this.style);
45144         }
45145         if(this.labelAlign){
45146             this.el.addClass('x-form-label-'+this.labelAlign);
45147         }
45148         if(this.hideLabels){
45149             this.labelStyle = "display:none";
45150             this.elementStyle = "padding-left:0;";
45151         }else{
45152             if(typeof this.labelWidth == 'number'){
45153                 this.labelStyle = "width:"+this.labelWidth+"px;";
45154                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45155             }
45156             if(this.labelAlign == 'top'){
45157                 this.labelStyle = "width:auto;";
45158                 this.elementStyle = "padding-left:0;";
45159             }
45160         }
45161         var stack = this.stack;
45162         var slen = stack.length;
45163         if(slen > 0){
45164             if(!this.fieldTpl){
45165                 var t = new Roo.Template(
45166                     '<div class="x-form-item {5}">',
45167                         '<label for="{0}" style="{2}">{1}{4}</label>',
45168                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45169                         '</div>',
45170                     '</div><div class="x-form-clear-left"></div>'
45171                 );
45172                 t.disableFormats = true;
45173                 t.compile();
45174                 Roo.form.Layout.prototype.fieldTpl = t;
45175             }
45176             for(var i = 0; i < slen; i++) {
45177                 if(stack[i].isFormField){
45178                     this.renderField(stack[i]);
45179                 }else{
45180                     this.renderComponent(stack[i]);
45181                 }
45182             }
45183         }
45184         if(this.clear){
45185             this.el.createChild({cls:'x-form-clear'});
45186         }
45187     },
45188
45189     // private
45190     renderField : function(f){
45191         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45192                f.id, //0
45193                f.fieldLabel, //1
45194                f.labelStyle||this.labelStyle||'', //2
45195                this.elementStyle||'', //3
45196                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45197                f.itemCls||this.itemCls||''  //5
45198        ], true).getPrevSibling());
45199     },
45200
45201     // private
45202     renderComponent : function(c){
45203         c.render(c.isLayout ? this.el : this.el.createChild());    
45204     },
45205     /**
45206      * Adds a object form elements (using the xtype property as the factory method.)
45207      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45208      * @param {Object} config 
45209      */
45210     addxtype : function(o)
45211     {
45212         // create the lement.
45213         o.form = this.form;
45214         var fe = Roo.factory(o, Roo.form);
45215         this.form.allItems.push(fe);
45216         this.stack.push(fe);
45217         
45218         if (fe.isFormField) {
45219             this.form.items.add(fe);
45220         }
45221          
45222         return fe;
45223     }
45224 });
45225
45226 /**
45227  * @class Roo.form.Column
45228  * @extends Roo.form.Layout
45229  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45230  * @constructor
45231  * @param {Object} config Configuration options
45232  */
45233 Roo.form.Column = function(config){
45234     Roo.form.Column.superclass.constructor.call(this, config);
45235 };
45236
45237 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45238     /**
45239      * @cfg {Number/String} width
45240      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45241      */
45242     /**
45243      * @cfg {String/Object} autoCreate
45244      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45245      */
45246
45247     // private
45248     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45249
45250     // private
45251     onRender : function(ct, position){
45252         Roo.form.Column.superclass.onRender.call(this, ct, position);
45253         if(this.width){
45254             this.el.setWidth(this.width);
45255         }
45256     }
45257 });
45258
45259
45260 /**
45261  * @class Roo.form.Row
45262  * @extends Roo.form.Layout
45263  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45264  * @constructor
45265  * @param {Object} config Configuration options
45266  */
45267
45268  
45269 Roo.form.Row = function(config){
45270     Roo.form.Row.superclass.constructor.call(this, config);
45271 };
45272  
45273 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45274       /**
45275      * @cfg {Number/String} width
45276      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45277      */
45278     /**
45279      * @cfg {Number/String} height
45280      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45281      */
45282     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45283     
45284     padWidth : 20,
45285     // private
45286     onRender : function(ct, position){
45287         //console.log('row render');
45288         if(!this.rowTpl){
45289             var t = new Roo.Template(
45290                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45291                     '<label for="{0}" style="{2}">{1}{4}</label>',
45292                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45293                     '</div>',
45294                 '</div>'
45295             );
45296             t.disableFormats = true;
45297             t.compile();
45298             Roo.form.Layout.prototype.rowTpl = t;
45299         }
45300         this.fieldTpl = this.rowTpl;
45301         
45302         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45303         var labelWidth = 100;
45304         
45305         if ((this.labelAlign != 'top')) {
45306             if (typeof this.labelWidth == 'number') {
45307                 labelWidth = this.labelWidth
45308             }
45309             this.padWidth =  20 + labelWidth;
45310             
45311         }
45312         
45313         Roo.form.Column.superclass.onRender.call(this, ct, position);
45314         if(this.width){
45315             this.el.setWidth(this.width);
45316         }
45317         if(this.height){
45318             this.el.setHeight(this.height);
45319         }
45320     },
45321     
45322     // private
45323     renderField : function(f){
45324         f.fieldEl = this.fieldTpl.append(this.el, [
45325                f.id, f.fieldLabel,
45326                f.labelStyle||this.labelStyle||'',
45327                this.elementStyle||'',
45328                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45329                f.itemCls||this.itemCls||'',
45330                f.width ? f.width + this.padWidth : 160 + this.padWidth
45331        ],true);
45332     }
45333 });
45334  
45335
45336 /**
45337  * @class Roo.form.FieldSet
45338  * @extends Roo.form.Layout
45339  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45340  * @constructor
45341  * @param {Object} config Configuration options
45342  */
45343 Roo.form.FieldSet = function(config){
45344     Roo.form.FieldSet.superclass.constructor.call(this, config);
45345 };
45346
45347 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45348     /**
45349      * @cfg {String} legend
45350      * The text to display as the legend for the FieldSet (defaults to '')
45351      */
45352     /**
45353      * @cfg {String/Object} autoCreate
45354      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45355      */
45356
45357     // private
45358     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45359
45360     // private
45361     onRender : function(ct, position){
45362         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45363         if(this.legend){
45364             this.setLegend(this.legend);
45365         }
45366     },
45367
45368     // private
45369     setLegend : function(text){
45370         if(this.rendered){
45371             this.el.child('legend').update(text);
45372         }
45373     }
45374 });/*
45375  * Based on:
45376  * Ext JS Library 1.1.1
45377  * Copyright(c) 2006-2007, Ext JS, LLC.
45378  *
45379  * Originally Released Under LGPL - original licence link has changed is not relivant.
45380  *
45381  * Fork - LGPL
45382  * <script type="text/javascript">
45383  */
45384 /**
45385  * @class Roo.form.VTypes
45386  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45387  * @singleton
45388  */
45389 Roo.form.VTypes = function(){
45390     // closure these in so they are only created once.
45391     var alpha = /^[a-zA-Z_]+$/;
45392     var alphanum = /^[a-zA-Z0-9_]+$/;
45393     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45394     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45395
45396     // All these messages and functions are configurable
45397     return {
45398         /**
45399          * The function used to validate email addresses
45400          * @param {String} value The email address
45401          */
45402         'email' : function(v){
45403             return email.test(v);
45404         },
45405         /**
45406          * The error text to display when the email validation function returns false
45407          * @type String
45408          */
45409         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45410         /**
45411          * The keystroke filter mask to be applied on email input
45412          * @type RegExp
45413          */
45414         'emailMask' : /[a-z0-9_\.\-@]/i,
45415
45416         /**
45417          * The function used to validate URLs
45418          * @param {String} value The URL
45419          */
45420         'url' : function(v){
45421             return url.test(v);
45422         },
45423         /**
45424          * The error text to display when the url validation function returns false
45425          * @type String
45426          */
45427         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45428         
45429         /**
45430          * The function used to validate alpha values
45431          * @param {String} value The value
45432          */
45433         'alpha' : function(v){
45434             return alpha.test(v);
45435         },
45436         /**
45437          * The error text to display when the alpha validation function returns false
45438          * @type String
45439          */
45440         'alphaText' : 'This field should only contain letters and _',
45441         /**
45442          * The keystroke filter mask to be applied on alpha input
45443          * @type RegExp
45444          */
45445         'alphaMask' : /[a-z_]/i,
45446
45447         /**
45448          * The function used to validate alphanumeric values
45449          * @param {String} value The value
45450          */
45451         'alphanum' : function(v){
45452             return alphanum.test(v);
45453         },
45454         /**
45455          * The error text to display when the alphanumeric validation function returns false
45456          * @type String
45457          */
45458         'alphanumText' : 'This field should only contain letters, numbers and _',
45459         /**
45460          * The keystroke filter mask to be applied on alphanumeric input
45461          * @type RegExp
45462          */
45463         'alphanumMask' : /[a-z0-9_]/i
45464     };
45465 }();//<script type="text/javascript">
45466
45467 /**
45468  * @class Roo.form.FCKeditor
45469  * @extends Roo.form.TextArea
45470  * Wrapper around the FCKEditor http://www.fckeditor.net
45471  * @constructor
45472  * Creates a new FCKeditor
45473  * @param {Object} config Configuration options
45474  */
45475 Roo.form.FCKeditor = function(config){
45476     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45477     this.addEvents({
45478          /**
45479          * @event editorinit
45480          * Fired when the editor is initialized - you can add extra handlers here..
45481          * @param {FCKeditor} this
45482          * @param {Object} the FCK object.
45483          */
45484         editorinit : true
45485     });
45486     
45487     
45488 };
45489 Roo.form.FCKeditor.editors = { };
45490 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45491 {
45492     //defaultAutoCreate : {
45493     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45494     //},
45495     // private
45496     /**
45497      * @cfg {Object} fck options - see fck manual for details.
45498      */
45499     fckconfig : false,
45500     
45501     /**
45502      * @cfg {Object} fck toolbar set (Basic or Default)
45503      */
45504     toolbarSet : 'Basic',
45505     /**
45506      * @cfg {Object} fck BasePath
45507      */ 
45508     basePath : '/fckeditor/',
45509     
45510     
45511     frame : false,
45512     
45513     value : '',
45514     
45515    
45516     onRender : function(ct, position)
45517     {
45518         if(!this.el){
45519             this.defaultAutoCreate = {
45520                 tag: "textarea",
45521                 style:"width:300px;height:60px;",
45522                 autocomplete: "off"
45523             };
45524         }
45525         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45526         /*
45527         if(this.grow){
45528             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45529             if(this.preventScrollbars){
45530                 this.el.setStyle("overflow", "hidden");
45531             }
45532             this.el.setHeight(this.growMin);
45533         }
45534         */
45535         //console.log('onrender' + this.getId() );
45536         Roo.form.FCKeditor.editors[this.getId()] = this;
45537          
45538
45539         this.replaceTextarea() ;
45540         
45541     },
45542     
45543     getEditor : function() {
45544         return this.fckEditor;
45545     },
45546     /**
45547      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45548      * @param {Mixed} value The value to set
45549      */
45550     
45551     
45552     setValue : function(value)
45553     {
45554         //console.log('setValue: ' + value);
45555         
45556         if(typeof(value) == 'undefined') { // not sure why this is happending...
45557             return;
45558         }
45559         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45560         
45561         //if(!this.el || !this.getEditor()) {
45562         //    this.value = value;
45563             //this.setValue.defer(100,this,[value]);    
45564         //    return;
45565         //} 
45566         
45567         if(!this.getEditor()) {
45568             return;
45569         }
45570         
45571         this.getEditor().SetData(value);
45572         
45573         //
45574
45575     },
45576
45577     /**
45578      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45579      * @return {Mixed} value The field value
45580      */
45581     getValue : function()
45582     {
45583         
45584         if (this.frame && this.frame.dom.style.display == 'none') {
45585             return Roo.form.FCKeditor.superclass.getValue.call(this);
45586         }
45587         
45588         if(!this.el || !this.getEditor()) {
45589            
45590            // this.getValue.defer(100,this); 
45591             return this.value;
45592         }
45593        
45594         
45595         var value=this.getEditor().GetData();
45596         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45597         return Roo.form.FCKeditor.superclass.getValue.call(this);
45598         
45599
45600     },
45601
45602     /**
45603      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45604      * @return {Mixed} value The field value
45605      */
45606     getRawValue : function()
45607     {
45608         if (this.frame && this.frame.dom.style.display == 'none') {
45609             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45610         }
45611         
45612         if(!this.el || !this.getEditor()) {
45613             //this.getRawValue.defer(100,this); 
45614             return this.value;
45615             return;
45616         }
45617         
45618         
45619         
45620         var value=this.getEditor().GetData();
45621         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45622         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45623          
45624     },
45625     
45626     setSize : function(w,h) {
45627         
45628         
45629         
45630         //if (this.frame && this.frame.dom.style.display == 'none') {
45631         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45632         //    return;
45633         //}
45634         //if(!this.el || !this.getEditor()) {
45635         //    this.setSize.defer(100,this, [w,h]); 
45636         //    return;
45637         //}
45638         
45639         
45640         
45641         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45642         
45643         this.frame.dom.setAttribute('width', w);
45644         this.frame.dom.setAttribute('height', h);
45645         this.frame.setSize(w,h);
45646         
45647     },
45648     
45649     toggleSourceEdit : function(value) {
45650         
45651       
45652          
45653         this.el.dom.style.display = value ? '' : 'none';
45654         this.frame.dom.style.display = value ?  'none' : '';
45655         
45656     },
45657     
45658     
45659     focus: function(tag)
45660     {
45661         if (this.frame.dom.style.display == 'none') {
45662             return Roo.form.FCKeditor.superclass.focus.call(this);
45663         }
45664         if(!this.el || !this.getEditor()) {
45665             this.focus.defer(100,this, [tag]); 
45666             return;
45667         }
45668         
45669         
45670         
45671         
45672         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45673         this.getEditor().Focus();
45674         if (tgs.length) {
45675             if (!this.getEditor().Selection.GetSelection()) {
45676                 this.focus.defer(100,this, [tag]); 
45677                 return;
45678             }
45679             
45680             
45681             var r = this.getEditor().EditorDocument.createRange();
45682             r.setStart(tgs[0],0);
45683             r.setEnd(tgs[0],0);
45684             this.getEditor().Selection.GetSelection().removeAllRanges();
45685             this.getEditor().Selection.GetSelection().addRange(r);
45686             this.getEditor().Focus();
45687         }
45688         
45689     },
45690     
45691     
45692     
45693     replaceTextarea : function()
45694     {
45695         if ( document.getElementById( this.getId() + '___Frame' ) )
45696             return ;
45697         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45698         //{
45699             // We must check the elements firstly using the Id and then the name.
45700         var oTextarea = document.getElementById( this.getId() );
45701         
45702         var colElementsByName = document.getElementsByName( this.getId() ) ;
45703          
45704         oTextarea.style.display = 'none' ;
45705
45706         if ( oTextarea.tabIndex ) {            
45707             this.TabIndex = oTextarea.tabIndex ;
45708         }
45709         
45710         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45711         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45712         this.frame = Roo.get(this.getId() + '___Frame')
45713     },
45714     
45715     _getConfigHtml : function()
45716     {
45717         var sConfig = '' ;
45718
45719         for ( var o in this.fckconfig ) {
45720             sConfig += sConfig.length > 0  ? '&amp;' : '';
45721             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45722         }
45723
45724         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45725     },
45726     
45727     
45728     _getIFrameHtml : function()
45729     {
45730         var sFile = 'fckeditor.html' ;
45731         /* no idea what this is about..
45732         try
45733         {
45734             if ( (/fcksource=true/i).test( window.top.location.search ) )
45735                 sFile = 'fckeditor.original.html' ;
45736         }
45737         catch (e) { 
45738         */
45739
45740         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45741         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45742         
45743         
45744         var html = '<iframe id="' + this.getId() +
45745             '___Frame" src="' + sLink +
45746             '" width="' + this.width +
45747             '" height="' + this.height + '"' +
45748             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45749             ' frameborder="0" scrolling="no"></iframe>' ;
45750
45751         return html ;
45752     },
45753     
45754     _insertHtmlBefore : function( html, element )
45755     {
45756         if ( element.insertAdjacentHTML )       {
45757             // IE
45758             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45759         } else { // Gecko
45760             var oRange = document.createRange() ;
45761             oRange.setStartBefore( element ) ;
45762             var oFragment = oRange.createContextualFragment( html );
45763             element.parentNode.insertBefore( oFragment, element ) ;
45764         }
45765     }
45766     
45767     
45768   
45769     
45770     
45771     
45772     
45773
45774 });
45775
45776 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45777
45778 function FCKeditor_OnComplete(editorInstance){
45779     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45780     f.fckEditor = editorInstance;
45781     //console.log("loaded");
45782     f.fireEvent('editorinit', f, editorInstance);
45783
45784   
45785
45786  
45787
45788
45789
45790
45791
45792
45793
45794
45795
45796
45797
45798
45799
45800
45801
45802 //<script type="text/javascript">
45803 /**
45804  * @class Roo.form.GridField
45805  * @extends Roo.form.Field
45806  * Embed a grid (or editable grid into a form)
45807  * STATUS ALPHA
45808  * 
45809  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45810  * it needs 
45811  * xgrid.store = Roo.data.Store
45812  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45813  * xgrid.store.reader = Roo.data.JsonReader 
45814  * 
45815  * 
45816  * @constructor
45817  * Creates a new GridField
45818  * @param {Object} config Configuration options
45819  */
45820 Roo.form.GridField = function(config){
45821     Roo.form.GridField.superclass.constructor.call(this, config);
45822      
45823 };
45824
45825 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45826     /**
45827      * @cfg {Number} width  - used to restrict width of grid..
45828      */
45829     width : 100,
45830     /**
45831      * @cfg {Number} height - used to restrict height of grid..
45832      */
45833     height : 50,
45834      /**
45835      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45836          * 
45837          *}
45838      */
45839     xgrid : false, 
45840     /**
45841      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45842      * {tag: "input", type: "checkbox", autocomplete: "off"})
45843      */
45844    // defaultAutoCreate : { tag: 'div' },
45845     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45846     /**
45847      * @cfg {String} addTitle Text to include for adding a title.
45848      */
45849     addTitle : false,
45850     //
45851     onResize : function(){
45852         Roo.form.Field.superclass.onResize.apply(this, arguments);
45853     },
45854
45855     initEvents : function(){
45856         // Roo.form.Checkbox.superclass.initEvents.call(this);
45857         // has no events...
45858        
45859     },
45860
45861
45862     getResizeEl : function(){
45863         return this.wrap;
45864     },
45865
45866     getPositionEl : function(){
45867         return this.wrap;
45868     },
45869
45870     // private
45871     onRender : function(ct, position){
45872         
45873         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45874         var style = this.style;
45875         delete this.style;
45876         
45877         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45878         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45879         this.viewEl = this.wrap.createChild({ tag: 'div' });
45880         if (style) {
45881             this.viewEl.applyStyles(style);
45882         }
45883         if (this.width) {
45884             this.viewEl.setWidth(this.width);
45885         }
45886         if (this.height) {
45887             this.viewEl.setHeight(this.height);
45888         }
45889         //if(this.inputValue !== undefined){
45890         //this.setValue(this.value);
45891         
45892         
45893         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45894         
45895         
45896         this.grid.render();
45897         this.grid.getDataSource().on('remove', this.refreshValue, this);
45898         this.grid.getDataSource().on('update', this.refreshValue, this);
45899         this.grid.on('afteredit', this.refreshValue, this);
45900  
45901     },
45902      
45903     
45904     /**
45905      * Sets the value of the item. 
45906      * @param {String} either an object  or a string..
45907      */
45908     setValue : function(v){
45909         //this.value = v;
45910         v = v || []; // empty set..
45911         // this does not seem smart - it really only affects memoryproxy grids..
45912         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45913             var ds = this.grid.getDataSource();
45914             // assumes a json reader..
45915             var data = {}
45916             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45917             ds.loadData( data);
45918         }
45919         // clear selection so it does not get stale.
45920         if (this.grid.sm) { 
45921             this.grid.sm.clearSelections();
45922         }
45923         
45924         Roo.form.GridField.superclass.setValue.call(this, v);
45925         this.refreshValue();
45926         // should load data in the grid really....
45927     },
45928     
45929     // private
45930     refreshValue: function() {
45931          var val = [];
45932         this.grid.getDataSource().each(function(r) {
45933             val.push(r.data);
45934         });
45935         this.el.dom.value = Roo.encode(val);
45936     }
45937     
45938      
45939     
45940     
45941 });/*
45942  * Based on:
45943  * Ext JS Library 1.1.1
45944  * Copyright(c) 2006-2007, Ext JS, LLC.
45945  *
45946  * Originally Released Under LGPL - original licence link has changed is not relivant.
45947  *
45948  * Fork - LGPL
45949  * <script type="text/javascript">
45950  */
45951 /**
45952  * @class Roo.form.DisplayField
45953  * @extends Roo.form.Field
45954  * A generic Field to display non-editable data.
45955  * @constructor
45956  * Creates a new Display Field item.
45957  * @param {Object} config Configuration options
45958  */
45959 Roo.form.DisplayField = function(config){
45960     Roo.form.DisplayField.superclass.constructor.call(this, config);
45961     
45962 };
45963
45964 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45965     inputType:      'hidden',
45966     allowBlank:     true,
45967     readOnly:         true,
45968     
45969  
45970     /**
45971      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45972      */
45973     focusClass : undefined,
45974     /**
45975      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45976      */
45977     fieldClass: 'x-form-field',
45978     
45979      /**
45980      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45981      */
45982     valueRenderer: undefined,
45983     
45984     width: 100,
45985     /**
45986      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45987      * {tag: "input", type: "checkbox", autocomplete: "off"})
45988      */
45989      
45990  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45991
45992     onResize : function(){
45993         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45994         
45995     },
45996
45997     initEvents : function(){
45998         // Roo.form.Checkbox.superclass.initEvents.call(this);
45999         // has no events...
46000        
46001     },
46002
46003
46004     getResizeEl : function(){
46005         return this.wrap;
46006     },
46007
46008     getPositionEl : function(){
46009         return this.wrap;
46010     },
46011
46012     // private
46013     onRender : function(ct, position){
46014         
46015         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46016         //if(this.inputValue !== undefined){
46017         this.wrap = this.el.wrap();
46018         
46019         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46020         
46021         if (this.bodyStyle) {
46022             this.viewEl.applyStyles(this.bodyStyle);
46023         }
46024         //this.viewEl.setStyle('padding', '2px');
46025         
46026         this.setValue(this.value);
46027         
46028     },
46029 /*
46030     // private
46031     initValue : Roo.emptyFn,
46032
46033   */
46034
46035         // private
46036     onClick : function(){
46037         
46038     },
46039
46040     /**
46041      * Sets the checked state of the checkbox.
46042      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46043      */
46044     setValue : function(v){
46045         this.value = v;
46046         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46047         // this might be called before we have a dom element..
46048         if (!this.viewEl) {
46049             return;
46050         }
46051         this.viewEl.dom.innerHTML = html;
46052         Roo.form.DisplayField.superclass.setValue.call(this, v);
46053
46054     }
46055 });/*
46056  * 
46057  * Licence- LGPL
46058  * 
46059  */
46060
46061 /**
46062  * @class Roo.form.DayPicker
46063  * @extends Roo.form.Field
46064  * A Day picker show [M] [T] [W] ....
46065  * @constructor
46066  * Creates a new Day Picker
46067  * @param {Object} config Configuration options
46068  */
46069 Roo.form.DayPicker= function(config){
46070     Roo.form.DayPicker.superclass.constructor.call(this, config);
46071      
46072 };
46073
46074 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46075     /**
46076      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46077      */
46078     focusClass : undefined,
46079     /**
46080      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46081      */
46082     fieldClass: "x-form-field",
46083    
46084     /**
46085      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46086      * {tag: "input", type: "checkbox", autocomplete: "off"})
46087      */
46088     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46089     
46090    
46091     actionMode : 'viewEl', 
46092     //
46093     // private
46094  
46095     inputType : 'hidden',
46096     
46097      
46098     inputElement: false, // real input element?
46099     basedOn: false, // ????
46100     
46101     isFormField: true, // not sure where this is needed!!!!
46102
46103     onResize : function(){
46104         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46105         if(!this.boxLabel){
46106             this.el.alignTo(this.wrap, 'c-c');
46107         }
46108     },
46109
46110     initEvents : function(){
46111         Roo.form.Checkbox.superclass.initEvents.call(this);
46112         this.el.on("click", this.onClick,  this);
46113         this.el.on("change", this.onClick,  this);
46114     },
46115
46116
46117     getResizeEl : function(){
46118         return this.wrap;
46119     },
46120
46121     getPositionEl : function(){
46122         return this.wrap;
46123     },
46124
46125     
46126     // private
46127     onRender : function(ct, position){
46128         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46129        
46130         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46131         
46132         var r1 = '<table><tr>';
46133         var r2 = '<tr class="x-form-daypick-icons">';
46134         for (var i=0; i < 7; i++) {
46135             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46136             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46137         }
46138         
46139         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46140         viewEl.select('img').on('click', this.onClick, this);
46141         this.viewEl = viewEl;   
46142         
46143         
46144         // this will not work on Chrome!!!
46145         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46146         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46147         
46148         
46149           
46150
46151     },
46152
46153     // private
46154     initValue : Roo.emptyFn,
46155
46156     /**
46157      * Returns the checked state of the checkbox.
46158      * @return {Boolean} True if checked, else false
46159      */
46160     getValue : function(){
46161         return this.el.dom.value;
46162         
46163     },
46164
46165         // private
46166     onClick : function(e){ 
46167         //this.setChecked(!this.checked);
46168         Roo.get(e.target).toggleClass('x-menu-item-checked');
46169         this.refreshValue();
46170         //if(this.el.dom.checked != this.checked){
46171         //    this.setValue(this.el.dom.checked);
46172        // }
46173     },
46174     
46175     // private
46176     refreshValue : function()
46177     {
46178         var val = '';
46179         this.viewEl.select('img',true).each(function(e,i,n)  {
46180             val += e.is(".x-menu-item-checked") ? String(n) : '';
46181         });
46182         this.setValue(val, true);
46183     },
46184
46185     /**
46186      * Sets the checked state of the checkbox.
46187      * On is always based on a string comparison between inputValue and the param.
46188      * @param {Boolean/String} value - the value to set 
46189      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46190      */
46191     setValue : function(v,suppressEvent){
46192         if (!this.el.dom) {
46193             return;
46194         }
46195         var old = this.el.dom.value ;
46196         this.el.dom.value = v;
46197         if (suppressEvent) {
46198             return ;
46199         }
46200          
46201         // update display..
46202         this.viewEl.select('img',true).each(function(e,i,n)  {
46203             
46204             var on = e.is(".x-menu-item-checked");
46205             var newv = v.indexOf(String(n)) > -1;
46206             if (on != newv) {
46207                 e.toggleClass('x-menu-item-checked');
46208             }
46209             
46210         });
46211         
46212         
46213         this.fireEvent('change', this, v, old);
46214         
46215         
46216     },
46217    
46218     // handle setting of hidden value by some other method!!?!?
46219     setFromHidden: function()
46220     {
46221         if(!this.el){
46222             return;
46223         }
46224         //console.log("SET FROM HIDDEN");
46225         //alert('setFrom hidden');
46226         this.setValue(this.el.dom.value);
46227     },
46228     
46229     onDestroy : function()
46230     {
46231         if(this.viewEl){
46232             Roo.get(this.viewEl).remove();
46233         }
46234          
46235         Roo.form.DayPicker.superclass.onDestroy.call(this);
46236     }
46237
46238 });/*
46239  * RooJS Library 1.1.1
46240  * Copyright(c) 2008-2011  Alan Knowles
46241  *
46242  * License - LGPL
46243  */
46244  
46245
46246 /**
46247  * @class Roo.form.ComboCheck
46248  * @extends Roo.form.ComboBox
46249  * A combobox for multiple select items.
46250  *
46251  * FIXME - could do with a reset button..
46252  * 
46253  * @constructor
46254  * Create a new ComboCheck
46255  * @param {Object} config Configuration options
46256  */
46257 Roo.form.ComboCheck = function(config){
46258     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46259     // should verify some data...
46260     // like
46261     // hiddenName = required..
46262     // displayField = required
46263     // valudField == required
46264     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46265     var _t = this;
46266     Roo.each(req, function(e) {
46267         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46268             throw "Roo.form.ComboCheck : missing value for: " + e;
46269         }
46270     });
46271     
46272     
46273 };
46274
46275 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46276      
46277      
46278     editable : false,
46279      
46280     selectedClass: 'x-menu-item-checked', 
46281     
46282     // private
46283     onRender : function(ct, position){
46284         var _t = this;
46285         
46286         
46287         
46288         if(!this.tpl){
46289             var cls = 'x-combo-list';
46290
46291             
46292             this.tpl =  new Roo.Template({
46293                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46294                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46295                    '<span>{' + this.displayField + '}</span>' +
46296                     '</div>' 
46297                 
46298             });
46299         }
46300  
46301         
46302         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46303         this.view.singleSelect = false;
46304         this.view.multiSelect = true;
46305         this.view.toggleSelect = true;
46306         this.pageTb.add(new Roo.Toolbar.Fill(), {
46307             
46308             text: 'Done',
46309             handler: function()
46310             {
46311                 _t.collapse();
46312             }
46313         });
46314     },
46315     
46316     onViewOver : function(e, t){
46317         // do nothing...
46318         return;
46319         
46320     },
46321     
46322     onViewClick : function(doFocus,index){
46323         return;
46324         
46325     },
46326     select: function () {
46327         //Roo.log("SELECT CALLED");
46328     },
46329      
46330     selectByValue : function(xv, scrollIntoView){
46331         var ar = this.getValueArray();
46332         var sels = [];
46333         
46334         Roo.each(ar, function(v) {
46335             if(v === undefined || v === null){
46336                 return;
46337             }
46338             var r = this.findRecord(this.valueField, v);
46339             if(r){
46340                 sels.push(this.store.indexOf(r))
46341                 
46342             }
46343         },this);
46344         this.view.select(sels);
46345         return false;
46346     },
46347     
46348     
46349     
46350     onSelect : function(record, index){
46351        // Roo.log("onselect Called");
46352        // this is only called by the clear button now..
46353         this.view.clearSelections();
46354         this.setValue('[]');
46355         if (this.value != this.valueBefore) {
46356             this.fireEvent('change', this, this.value, this.valueBefore);
46357             this.valueBefore = this.value;
46358         }
46359     },
46360     getValueArray : function()
46361     {
46362         var ar = [] ;
46363         
46364         try {
46365             //Roo.log(this.value);
46366             if (typeof(this.value) == 'undefined') {
46367                 return [];
46368             }
46369             var ar = Roo.decode(this.value);
46370             return  ar instanceof Array ? ar : []; //?? valid?
46371             
46372         } catch(e) {
46373             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46374             return [];
46375         }
46376          
46377     },
46378     expand : function ()
46379     {
46380         
46381         Roo.form.ComboCheck.superclass.expand.call(this);
46382         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46383         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46384         
46385
46386     },
46387     
46388     collapse : function(){
46389         Roo.form.ComboCheck.superclass.collapse.call(this);
46390         var sl = this.view.getSelectedIndexes();
46391         var st = this.store;
46392         var nv = [];
46393         var tv = [];
46394         var r;
46395         Roo.each(sl, function(i) {
46396             r = st.getAt(i);
46397             nv.push(r.get(this.valueField));
46398         },this);
46399         this.setValue(Roo.encode(nv));
46400         if (this.value != this.valueBefore) {
46401
46402             this.fireEvent('change', this, this.value, this.valueBefore);
46403             this.valueBefore = this.value;
46404         }
46405         
46406     },
46407     
46408     setValue : function(v){
46409         // Roo.log(v);
46410         this.value = v;
46411         
46412         var vals = this.getValueArray();
46413         var tv = [];
46414         Roo.each(vals, function(k) {
46415             var r = this.findRecord(this.valueField, k);
46416             if(r){
46417                 tv.push(r.data[this.displayField]);
46418             }else if(this.valueNotFoundText !== undefined){
46419                 tv.push( this.valueNotFoundText );
46420             }
46421         },this);
46422        // Roo.log(tv);
46423         
46424         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46425         this.hiddenField.value = v;
46426         this.value = v;
46427     }
46428     
46429 });/*
46430  * Based on:
46431  * Ext JS Library 1.1.1
46432  * Copyright(c) 2006-2007, Ext JS, LLC.
46433  *
46434  * Originally Released Under LGPL - original licence link has changed is not relivant.
46435  *
46436  * Fork - LGPL
46437  * <script type="text/javascript">
46438  */
46439  
46440 /**
46441  * @class Roo.form.Signature
46442  * @extends Roo.form.Field
46443  * Signature field.  
46444  * @constructor
46445  * 
46446  * @param {Object} config Configuration options
46447  */
46448
46449 Roo.form.Signature = function(config){
46450     Roo.form.Signature.superclass.constructor.call(this, config);
46451     
46452     this.addEvents({// not in used??
46453          /**
46454          * @event confirm
46455          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46456              * @param {Roo.form.Signature} combo This combo box
46457              */
46458         'confirm' : true,
46459         /**
46460          * @event reset
46461          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46462              * @param {Roo.form.ComboBox} combo This combo box
46463              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46464              */
46465         'reset' : true
46466     });
46467 };
46468
46469 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46470     /**
46471      * @cfg {Object} labels Label to use when rendering a form.
46472      * defaults to 
46473      * labels : { 
46474      *      clear : "Clear",
46475      *      confirm : "Confirm"
46476      *  }
46477      */
46478     labels : { 
46479         clear : "Clear",
46480         confirm : "Confirm"
46481     },
46482     /**
46483      * @cfg {Number} width The signature panel width (defaults to 300)
46484      */
46485     width: 300,
46486     /**
46487      * @cfg {Number} height The signature panel height (defaults to 100)
46488      */
46489     height : 100,
46490     /**
46491      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46492      */
46493     allowBlank : false,
46494     
46495     //private
46496     // {Object} signPanel The signature SVG panel element (defaults to {})
46497     signPanel : {},
46498     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46499     isMouseDown : false,
46500     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46501     isConfirmed : false,
46502     // {String} signatureTmp SVG mapping string (defaults to empty string)
46503     signatureTmp : '',
46504     
46505     
46506     defaultAutoCreate : { // modified by initCompnoent..
46507         tag: "input",
46508         type:"hidden"
46509     },
46510
46511     // private
46512     onRender : function(ct, position){
46513         
46514         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46515         
46516         this.wrap = this.el.wrap({
46517             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46518         });
46519         
46520         this.createToolbar(this);
46521         this.signPanel = this.wrap.createChild({
46522                 tag: 'div',
46523                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46524             }, this.el
46525         );
46526             
46527         this.svgID = Roo.id();
46528         this.svgEl = this.signPanel.createChild({
46529               xmlns : 'http://www.w3.org/2000/svg',
46530               tag : 'svg',
46531               id : this.svgID + "-svg",
46532               width: this.width,
46533               height: this.height,
46534               viewBox: '0 0 '+this.width+' '+this.height,
46535               cn : [
46536                 {
46537                     tag: "rect",
46538                     id: this.svgID + "-svg-r",
46539                     width: this.width,
46540                     height: this.height,
46541                     fill: "#ffa"
46542                 },
46543                 {
46544                     tag: "line",
46545                     id: this.svgID + "-svg-l",
46546                     x1: "0", // start
46547                     y1: (this.height*0.8), // start set the line in 80% of height
46548                     x2: this.width, // end
46549                     y2: (this.height*0.8), // end set the line in 80% of height
46550                     'stroke': "#666",
46551                     'stroke-width': "1",
46552                     'stroke-dasharray': "3",
46553                     'shape-rendering': "crispEdges",
46554                     'pointer-events': "none"
46555                 },
46556                 {
46557                     tag: "path",
46558                     id: this.svgID + "-svg-p",
46559                     'stroke': "navy",
46560                     'stroke-width': "3",
46561                     'fill': "none",
46562                     'pointer-events': 'none'
46563                 }
46564               ]
46565         });
46566         this.createSVG();
46567         this.svgBox = this.svgEl.dom.getScreenCTM();
46568     },
46569     createSVG : function(){ 
46570         var svg = this.signPanel;
46571         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46572         var t = this;
46573
46574         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46575         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46576         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46577         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46578         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46579         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46580         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46581         
46582     },
46583     isTouchEvent : function(e){
46584         return e.type.match(/^touch/);
46585     },
46586     getCoords : function (e) {
46587         var pt    = this.svgEl.dom.createSVGPoint();
46588         pt.x = e.clientX; 
46589         pt.y = e.clientY;
46590         if (this.isTouchEvent(e)) {
46591             pt.x =  e.targetTouches[0].clientX 
46592             pt.y = e.targetTouches[0].clientY;
46593         }
46594         var a = this.svgEl.dom.getScreenCTM();
46595         var b = a.inverse();
46596         var mx = pt.matrixTransform(b);
46597         return mx.x + ',' + mx.y;
46598     },
46599     //mouse event headler 
46600     down : function (e) {
46601         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46602         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46603         
46604         this.isMouseDown = true;
46605         
46606         e.preventDefault();
46607     },
46608     move : function (e) {
46609         if (this.isMouseDown) {
46610             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46611             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46612         }
46613         
46614         e.preventDefault();
46615     },
46616     up : function (e) {
46617         this.isMouseDown = false;
46618         var sp = this.signatureTmp.split(' ');
46619         
46620         if(sp.length > 1){
46621             if(!sp[sp.length-2].match(/^L/)){
46622                 sp.pop();
46623                 sp.pop();
46624                 sp.push("");
46625                 this.signatureTmp = sp.join(" ");
46626             }
46627         }
46628         if(this.getValue() != this.signatureTmp){
46629             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46630             this.isConfirmed = false;
46631         }
46632         e.preventDefault();
46633     },
46634     
46635     /**
46636      * Protected method that will not generally be called directly. It
46637      * is called when the editor creates its toolbar. Override this method if you need to
46638      * add custom toolbar buttons.
46639      * @param {HtmlEditor} editor
46640      */
46641     createToolbar : function(editor){
46642          function btn(id, toggle, handler){
46643             var xid = fid + '-'+ id ;
46644             return {
46645                 id : xid,
46646                 cmd : id,
46647                 cls : 'x-btn-icon x-edit-'+id,
46648                 enableToggle:toggle !== false,
46649                 scope: editor, // was editor...
46650                 handler:handler||editor.relayBtnCmd,
46651                 clickEvent:'mousedown',
46652                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46653                 tabIndex:-1
46654             };
46655         }
46656         
46657         
46658         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46659         this.tb = tb;
46660         this.tb.add(
46661            {
46662                 cls : ' x-signature-btn x-signature-'+id,
46663                 scope: editor, // was editor...
46664                 handler: this.reset,
46665                 clickEvent:'mousedown',
46666                 text: this.labels.clear
46667             },
46668             {
46669                  xtype : 'Fill',
46670                  xns: Roo.Toolbar
46671             }, 
46672             {
46673                 cls : '  x-signature-btn x-signature-'+id,
46674                 scope: editor, // was editor...
46675                 handler: this.confirmHandler,
46676                 clickEvent:'mousedown',
46677                 text: this.labels.confirm
46678             }
46679         );
46680     
46681     },
46682     //public
46683     /**
46684      * when user is clicked confirm then show this image.....
46685      * 
46686      * @return {String} Image Data URI
46687      */
46688     getImageDataURI : function(){
46689         var svg = this.svgEl.dom.parentNode.innerHTML;
46690         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46691         return src; 
46692     },
46693     /**
46694      * 
46695      * @return {Boolean} this.isConfirmed
46696      */
46697     getConfirmed : function(){
46698         return this.isConfirmed;
46699     },
46700     /**
46701      * 
46702      * @return {Number} this.width
46703      */
46704     getWidth : function(){
46705         return this.width;
46706     },
46707     /**
46708      * 
46709      * @return {Number} this.height
46710      */
46711     getHeight : function(){
46712         return this.height;
46713     },
46714     // private
46715     getSignature : function(){
46716         return this.signatureTmp;
46717     },
46718     // private
46719     reset : function(){
46720         this.signatureTmp = '';
46721         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46722         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46723         this.isConfirmed = false;
46724         Roo.form.Signature.superclass.reset.call(this);
46725     },
46726     setSignature : function(s){
46727         this.signatureTmp = s;
46728         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46729         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46730         this.setValue(s);
46731         this.isConfirmed = false;
46732         Roo.form.Signature.superclass.reset.call(this);
46733     }, 
46734     test : function(){
46735 //        Roo.log(this.signPanel.dom.contentWindow.up())
46736     },
46737     //private
46738     setConfirmed : function(){
46739         
46740         
46741         
46742 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46743     },
46744     // private
46745     confirmHandler : function(){
46746         if(!this.getSignature()){
46747             return;
46748         }
46749         
46750         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46751         this.setValue(this.getSignature());
46752         this.isConfirmed = true;
46753         
46754         this.fireEvent('confirm', this);
46755     },
46756     // private
46757     // Subclasses should provide the validation implementation by overriding this
46758     validateValue : function(value){
46759         if(this.allowBlank){
46760             return true;
46761         }
46762         
46763         if(this.isConfirmed){
46764             return true;
46765         }
46766         return false;
46767     }
46768 });/*
46769  * Based on:
46770  * Ext JS Library 1.1.1
46771  * Copyright(c) 2006-2007, Ext JS, LLC.
46772  *
46773  * Originally Released Under LGPL - original licence link has changed is not relivant.
46774  *
46775  * Fork - LGPL
46776  * <script type="text/javascript">
46777  */
46778  
46779
46780 /**
46781  * @class Roo.form.ComboBox
46782  * @extends Roo.form.TriggerField
46783  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46784  * @constructor
46785  * Create a new ComboBox.
46786  * @param {Object} config Configuration options
46787  */
46788 Roo.form.Select = function(config){
46789     Roo.form.Select.superclass.constructor.call(this, config);
46790      
46791 };
46792
46793 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46794     /**
46795      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46796      */
46797     /**
46798      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46799      * rendering into an Roo.Editor, defaults to false)
46800      */
46801     /**
46802      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46803      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46804      */
46805     /**
46806      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46807      */
46808     /**
46809      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46810      * the dropdown list (defaults to undefined, with no header element)
46811      */
46812
46813      /**
46814      * @cfg {String/Roo.Template} tpl The template to use to render the output
46815      */
46816      
46817     // private
46818     defaultAutoCreate : {tag: "select"  },
46819     /**
46820      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46821      */
46822     listWidth: undefined,
46823     /**
46824      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46825      * mode = 'remote' or 'text' if mode = 'local')
46826      */
46827     displayField: undefined,
46828     /**
46829      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46830      * mode = 'remote' or 'value' if mode = 'local'). 
46831      * Note: use of a valueField requires the user make a selection
46832      * in order for a value to be mapped.
46833      */
46834     valueField: undefined,
46835     
46836     
46837     /**
46838      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46839      * field's data value (defaults to the underlying DOM element's name)
46840      */
46841     hiddenName: undefined,
46842     /**
46843      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46844      */
46845     listClass: '',
46846     /**
46847      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46848      */
46849     selectedClass: 'x-combo-selected',
46850     /**
46851      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46852      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46853      * which displays a downward arrow icon).
46854      */
46855     triggerClass : 'x-form-arrow-trigger',
46856     /**
46857      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46858      */
46859     shadow:'sides',
46860     /**
46861      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46862      * anchor positions (defaults to 'tl-bl')
46863      */
46864     listAlign: 'tl-bl?',
46865     /**
46866      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46867      */
46868     maxHeight: 300,
46869     /**
46870      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46871      * query specified by the allQuery config option (defaults to 'query')
46872      */
46873     triggerAction: 'query',
46874     /**
46875      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46876      * (defaults to 4, does not apply if editable = false)
46877      */
46878     minChars : 4,
46879     /**
46880      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46881      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46882      */
46883     typeAhead: false,
46884     /**
46885      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46886      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46887      */
46888     queryDelay: 500,
46889     /**
46890      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46891      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46892      */
46893     pageSize: 0,
46894     /**
46895      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46896      * when editable = true (defaults to false)
46897      */
46898     selectOnFocus:false,
46899     /**
46900      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46901      */
46902     queryParam: 'query',
46903     /**
46904      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46905      * when mode = 'remote' (defaults to 'Loading...')
46906      */
46907     loadingText: 'Loading...',
46908     /**
46909      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46910      */
46911     resizable: false,
46912     /**
46913      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46914      */
46915     handleHeight : 8,
46916     /**
46917      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46918      * traditional select (defaults to true)
46919      */
46920     editable: true,
46921     /**
46922      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46923      */
46924     allQuery: '',
46925     /**
46926      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46927      */
46928     mode: 'remote',
46929     /**
46930      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46931      * listWidth has a higher value)
46932      */
46933     minListWidth : 70,
46934     /**
46935      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46936      * allow the user to set arbitrary text into the field (defaults to false)
46937      */
46938     forceSelection:false,
46939     /**
46940      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46941      * if typeAhead = true (defaults to 250)
46942      */
46943     typeAheadDelay : 250,
46944     /**
46945      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46946      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46947      */
46948     valueNotFoundText : undefined,
46949     
46950     /**
46951      * @cfg {String} defaultValue The value displayed after loading the store.
46952      */
46953     defaultValue: '',
46954     
46955     /**
46956      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46957      */
46958     blockFocus : false,
46959     
46960     /**
46961      * @cfg {Boolean} disableClear Disable showing of clear button.
46962      */
46963     disableClear : false,
46964     /**
46965      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46966      */
46967     alwaysQuery : false,
46968     
46969     //private
46970     addicon : false,
46971     editicon: false,
46972     
46973     // element that contains real text value.. (when hidden is used..)
46974      
46975     // private
46976     onRender : function(ct, position){
46977         Roo.form.Field.prototype.onRender.call(this, ct, position);
46978         
46979         if(this.store){
46980             this.store.on('beforeload', this.onBeforeLoad, this);
46981             this.store.on('load', this.onLoad, this);
46982             this.store.on('loadexception', this.onLoadException, this);
46983             this.store.load({});
46984         }
46985         
46986         
46987         
46988     },
46989
46990     // private
46991     initEvents : function(){
46992         //Roo.form.ComboBox.superclass.initEvents.call(this);
46993  
46994     },
46995
46996     onDestroy : function(){
46997        
46998         if(this.store){
46999             this.store.un('beforeload', this.onBeforeLoad, this);
47000             this.store.un('load', this.onLoad, this);
47001             this.store.un('loadexception', this.onLoadException, this);
47002         }
47003         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47004     },
47005
47006     // private
47007     fireKey : function(e){
47008         if(e.isNavKeyPress() && !this.list.isVisible()){
47009             this.fireEvent("specialkey", this, e);
47010         }
47011     },
47012
47013     // private
47014     onResize: function(w, h){
47015         
47016         return; 
47017     
47018         
47019     },
47020
47021     /**
47022      * Allow or prevent the user from directly editing the field text.  If false is passed,
47023      * the user will only be able to select from the items defined in the dropdown list.  This method
47024      * is the runtime equivalent of setting the 'editable' config option at config time.
47025      * @param {Boolean} value True to allow the user to directly edit the field text
47026      */
47027     setEditable : function(value){
47028          
47029     },
47030
47031     // private
47032     onBeforeLoad : function(){
47033         
47034         Roo.log("Select before load");
47035         return;
47036     
47037         this.innerList.update(this.loadingText ?
47038                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47039         //this.restrictHeight();
47040         this.selectedIndex = -1;
47041     },
47042
47043     // private
47044     onLoad : function(){
47045
47046     
47047         var dom = this.el.dom;
47048         dom.innerHTML = '';
47049          var od = dom.ownerDocument;
47050          
47051         if (this.emptyText) {
47052             var op = od.createElement('option');
47053             op.setAttribute('value', '');
47054             op.innerHTML = String.format('{0}', this.emptyText);
47055             dom.appendChild(op);
47056         }
47057         if(this.store.getCount() > 0){
47058            
47059             var vf = this.valueField;
47060             var df = this.displayField;
47061             this.store.data.each(function(r) {
47062                 // which colmsn to use... testing - cdoe / title..
47063                 var op = od.createElement('option');
47064                 op.setAttribute('value', r.data[vf]);
47065                 op.innerHTML = String.format('{0}', r.data[df]);
47066                 dom.appendChild(op);
47067             });
47068             if (typeof(this.defaultValue != 'undefined')) {
47069                 this.setValue(this.defaultValue);
47070             }
47071             
47072              
47073         }else{
47074             //this.onEmptyResults();
47075         }
47076         //this.el.focus();
47077     },
47078     // private
47079     onLoadException : function()
47080     {
47081         dom.innerHTML = '';
47082             
47083         Roo.log("Select on load exception");
47084         return;
47085     
47086         this.collapse();
47087         Roo.log(this.store.reader.jsonData);
47088         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47089             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47090         }
47091         
47092         
47093     },
47094     // private
47095     onTypeAhead : function(){
47096          
47097     },
47098
47099     // private
47100     onSelect : function(record, index){
47101         Roo.log('on select?');
47102         return;
47103         if(this.fireEvent('beforeselect', this, record, index) !== false){
47104             this.setFromData(index > -1 ? record.data : false);
47105             this.collapse();
47106             this.fireEvent('select', this, record, index);
47107         }
47108     },
47109
47110     /**
47111      * Returns the currently selected field value or empty string if no value is set.
47112      * @return {String} value The selected value
47113      */
47114     getValue : function(){
47115         var dom = this.el.dom;
47116         this.value = dom.options[dom.selectedIndex].value;
47117         return this.value;
47118         
47119     },
47120
47121     /**
47122      * Clears any text/value currently set in the field
47123      */
47124     clearValue : function(){
47125         this.value = '';
47126         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47127         
47128     },
47129
47130     /**
47131      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47132      * will be displayed in the field.  If the value does not match the data value of an existing item,
47133      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47134      * Otherwise the field will be blank (although the value will still be set).
47135      * @param {String} value The value to match
47136      */
47137     setValue : function(v){
47138         var d = this.el.dom;
47139         for (var i =0; i < d.options.length;i++) {
47140             if (v == d.options[i].value) {
47141                 d.selectedIndex = i;
47142                 this.value = v;
47143                 return;
47144             }
47145         }
47146         this.clearValue();
47147     },
47148     /**
47149      * @property {Object} the last set data for the element
47150      */
47151     
47152     lastData : false,
47153     /**
47154      * Sets the value of the field based on a object which is related to the record format for the store.
47155      * @param {Object} value the value to set as. or false on reset?
47156      */
47157     setFromData : function(o){
47158         Roo.log('setfrom data?');
47159          
47160         
47161         
47162     },
47163     // private
47164     reset : function(){
47165         this.clearValue();
47166     },
47167     // private
47168     findRecord : function(prop, value){
47169         
47170         return false;
47171     
47172         var record;
47173         if(this.store.getCount() > 0){
47174             this.store.each(function(r){
47175                 if(r.data[prop] == value){
47176                     record = r;
47177                     return false;
47178                 }
47179                 return true;
47180             });
47181         }
47182         return record;
47183     },
47184     
47185     getName: function()
47186     {
47187         // returns hidden if it's set..
47188         if (!this.rendered) {return ''};
47189         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47190         
47191     },
47192      
47193
47194     
47195
47196     // private
47197     onEmptyResults : function(){
47198         Roo.log('empty results');
47199         //this.collapse();
47200     },
47201
47202     /**
47203      * Returns true if the dropdown list is expanded, else false.
47204      */
47205     isExpanded : function(){
47206         return false;
47207     },
47208
47209     /**
47210      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47211      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47212      * @param {String} value The data value of the item to select
47213      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47214      * selected item if it is not currently in view (defaults to true)
47215      * @return {Boolean} True if the value matched an item in the list, else false
47216      */
47217     selectByValue : function(v, scrollIntoView){
47218         Roo.log('select By Value');
47219         return false;
47220     
47221         if(v !== undefined && v !== null){
47222             var r = this.findRecord(this.valueField || this.displayField, v);
47223             if(r){
47224                 this.select(this.store.indexOf(r), scrollIntoView);
47225                 return true;
47226             }
47227         }
47228         return false;
47229     },
47230
47231     /**
47232      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47233      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47234      * @param {Number} index The zero-based index of the list item to select
47235      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47236      * selected item if it is not currently in view (defaults to true)
47237      */
47238     select : function(index, scrollIntoView){
47239         Roo.log('select ');
47240         return  ;
47241         
47242         this.selectedIndex = index;
47243         this.view.select(index);
47244         if(scrollIntoView !== false){
47245             var el = this.view.getNode(index);
47246             if(el){
47247                 this.innerList.scrollChildIntoView(el, false);
47248             }
47249         }
47250     },
47251
47252       
47253
47254     // private
47255     validateBlur : function(){
47256         
47257         return;
47258         
47259     },
47260
47261     // private
47262     initQuery : function(){
47263         this.doQuery(this.getRawValue());
47264     },
47265
47266     // private
47267     doForce : function(){
47268         if(this.el.dom.value.length > 0){
47269             this.el.dom.value =
47270                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47271              
47272         }
47273     },
47274
47275     /**
47276      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47277      * query allowing the query action to be canceled if needed.
47278      * @param {String} query The SQL query to execute
47279      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47280      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47281      * saved in the current store (defaults to false)
47282      */
47283     doQuery : function(q, forceAll){
47284         
47285         Roo.log('doQuery?');
47286         if(q === undefined || q === null){
47287             q = '';
47288         }
47289         var qe = {
47290             query: q,
47291             forceAll: forceAll,
47292             combo: this,
47293             cancel:false
47294         };
47295         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47296             return false;
47297         }
47298         q = qe.query;
47299         forceAll = qe.forceAll;
47300         if(forceAll === true || (q.length >= this.minChars)){
47301             if(this.lastQuery != q || this.alwaysQuery){
47302                 this.lastQuery = q;
47303                 if(this.mode == 'local'){
47304                     this.selectedIndex = -1;
47305                     if(forceAll){
47306                         this.store.clearFilter();
47307                     }else{
47308                         this.store.filter(this.displayField, q);
47309                     }
47310                     this.onLoad();
47311                 }else{
47312                     this.store.baseParams[this.queryParam] = q;
47313                     this.store.load({
47314                         params: this.getParams(q)
47315                     });
47316                     this.expand();
47317                 }
47318             }else{
47319                 this.selectedIndex = -1;
47320                 this.onLoad();   
47321             }
47322         }
47323     },
47324
47325     // private
47326     getParams : function(q){
47327         var p = {};
47328         //p[this.queryParam] = q;
47329         if(this.pageSize){
47330             p.start = 0;
47331             p.limit = this.pageSize;
47332         }
47333         return p;
47334     },
47335
47336     /**
47337      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47338      */
47339     collapse : function(){
47340         
47341     },
47342
47343     // private
47344     collapseIf : function(e){
47345         
47346     },
47347
47348     /**
47349      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47350      */
47351     expand : function(){
47352         
47353     } ,
47354
47355     // private
47356      
47357
47358     /** 
47359     * @cfg {Boolean} grow 
47360     * @hide 
47361     */
47362     /** 
47363     * @cfg {Number} growMin 
47364     * @hide 
47365     */
47366     /** 
47367     * @cfg {Number} growMax 
47368     * @hide 
47369     */
47370     /**
47371      * @hide
47372      * @method autoSize
47373      */
47374     
47375     setWidth : function()
47376     {
47377         
47378     },
47379     getResizeEl : function(){
47380         return this.el;
47381     }
47382 });//<script type="text/javasscript">
47383  
47384
47385 /**
47386  * @class Roo.DDView
47387  * A DnD enabled version of Roo.View.
47388  * @param {Element/String} container The Element in which to create the View.
47389  * @param {String} tpl The template string used to create the markup for each element of the View
47390  * @param {Object} config The configuration properties. These include all the config options of
47391  * {@link Roo.View} plus some specific to this class.<br>
47392  * <p>
47393  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47394  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47395  * <p>
47396  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47397 .x-view-drag-insert-above {
47398         border-top:1px dotted #3366cc;
47399 }
47400 .x-view-drag-insert-below {
47401         border-bottom:1px dotted #3366cc;
47402 }
47403 </code></pre>
47404  * 
47405  */
47406  
47407 Roo.DDView = function(container, tpl, config) {
47408     Roo.DDView.superclass.constructor.apply(this, arguments);
47409     this.getEl().setStyle("outline", "0px none");
47410     this.getEl().unselectable();
47411     if (this.dragGroup) {
47412                 this.setDraggable(this.dragGroup.split(","));
47413     }
47414     if (this.dropGroup) {
47415                 this.setDroppable(this.dropGroup.split(","));
47416     }
47417     if (this.deletable) {
47418         this.setDeletable();
47419     }
47420     this.isDirtyFlag = false;
47421         this.addEvents({
47422                 "drop" : true
47423         });
47424 };
47425
47426 Roo.extend(Roo.DDView, Roo.View, {
47427 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47428 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47429 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47430 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47431
47432         isFormField: true,
47433
47434         reset: Roo.emptyFn,
47435         
47436         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47437
47438         validate: function() {
47439                 return true;
47440         },
47441         
47442         destroy: function() {
47443                 this.purgeListeners();
47444                 this.getEl.removeAllListeners();
47445                 this.getEl().remove();
47446                 if (this.dragZone) {
47447                         if (this.dragZone.destroy) {
47448                                 this.dragZone.destroy();
47449                         }
47450                 }
47451                 if (this.dropZone) {
47452                         if (this.dropZone.destroy) {
47453                                 this.dropZone.destroy();
47454                         }
47455                 }
47456         },
47457
47458 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47459         getName: function() {
47460                 return this.name;
47461         },
47462
47463 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47464         setValue: function(v) {
47465                 if (!this.store) {
47466                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47467                 }
47468                 var data = {};
47469                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47470                 this.store.proxy = new Roo.data.MemoryProxy(data);
47471                 this.store.load();
47472         },
47473
47474 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47475         getValue: function() {
47476                 var result = '(';
47477                 this.store.each(function(rec) {
47478                         result += rec.id + ',';
47479                 });
47480                 return result.substr(0, result.length - 1) + ')';
47481         },
47482         
47483         getIds: function() {
47484                 var i = 0, result = new Array(this.store.getCount());
47485                 this.store.each(function(rec) {
47486                         result[i++] = rec.id;
47487                 });
47488                 return result;
47489         },
47490         
47491         isDirty: function() {
47492                 return this.isDirtyFlag;
47493         },
47494
47495 /**
47496  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47497  *      whole Element becomes the target, and this causes the drop gesture to append.
47498  */
47499     getTargetFromEvent : function(e) {
47500                 var target = e.getTarget();
47501                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47502                 target = target.parentNode;
47503                 }
47504                 if (!target) {
47505                         target = this.el.dom.lastChild || this.el.dom;
47506                 }
47507                 return target;
47508     },
47509
47510 /**
47511  *      Create the drag data which consists of an object which has the property "ddel" as
47512  *      the drag proxy element. 
47513  */
47514     getDragData : function(e) {
47515         var target = this.findItemFromChild(e.getTarget());
47516                 if(target) {
47517                         this.handleSelection(e);
47518                         var selNodes = this.getSelectedNodes();
47519             var dragData = {
47520                 source: this,
47521                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47522                 nodes: selNodes,
47523                 records: []
47524                         };
47525                         var selectedIndices = this.getSelectedIndexes();
47526                         for (var i = 0; i < selectedIndices.length; i++) {
47527                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47528                         }
47529                         if (selNodes.length == 1) {
47530                                 dragData.ddel = target.cloneNode(true); // the div element
47531                         } else {
47532                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47533                                 div.className = 'multi-proxy';
47534                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47535                                         div.appendChild(selNodes[i].cloneNode(true));
47536                                 }
47537                                 dragData.ddel = div;
47538                         }
47539             //console.log(dragData)
47540             //console.log(dragData.ddel.innerHTML)
47541                         return dragData;
47542                 }
47543         //console.log('nodragData')
47544                 return false;
47545     },
47546     
47547 /**     Specify to which ddGroup items in this DDView may be dragged. */
47548     setDraggable: function(ddGroup) {
47549         if (ddGroup instanceof Array) {
47550                 Roo.each(ddGroup, this.setDraggable, this);
47551                 return;
47552         }
47553         if (this.dragZone) {
47554                 this.dragZone.addToGroup(ddGroup);
47555         } else {
47556                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47557                                 containerScroll: true,
47558                                 ddGroup: ddGroup 
47559
47560                         });
47561 //                      Draggability implies selection. DragZone's mousedown selects the element.
47562                         if (!this.multiSelect) { this.singleSelect = true; }
47563
47564 //                      Wire the DragZone's handlers up to methods in *this*
47565                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47566                 }
47567     },
47568
47569 /**     Specify from which ddGroup this DDView accepts drops. */
47570     setDroppable: function(ddGroup) {
47571         if (ddGroup instanceof Array) {
47572                 Roo.each(ddGroup, this.setDroppable, this);
47573                 return;
47574         }
47575         if (this.dropZone) {
47576                 this.dropZone.addToGroup(ddGroup);
47577         } else {
47578                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47579                                 containerScroll: true,
47580                                 ddGroup: ddGroup
47581                         });
47582
47583 //                      Wire the DropZone's handlers up to methods in *this*
47584                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47585                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47586                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47587                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47588                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47589                 }
47590     },
47591
47592 /**     Decide whether to drop above or below a View node. */
47593     getDropPoint : function(e, n, dd){
47594         if (n == this.el.dom) { return "above"; }
47595                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47596                 var c = t + (b - t) / 2;
47597                 var y = Roo.lib.Event.getPageY(e);
47598                 if(y <= c) {
47599                         return "above";
47600                 }else{
47601                         return "below";
47602                 }
47603     },
47604
47605     onNodeEnter : function(n, dd, e, data){
47606                 return false;
47607     },
47608     
47609     onNodeOver : function(n, dd, e, data){
47610                 var pt = this.getDropPoint(e, n, dd);
47611                 // set the insert point style on the target node
47612                 var dragElClass = this.dropNotAllowed;
47613                 if (pt) {
47614                         var targetElClass;
47615                         if (pt == "above"){
47616                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47617                                 targetElClass = "x-view-drag-insert-above";
47618                         } else {
47619                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47620                                 targetElClass = "x-view-drag-insert-below";
47621                         }
47622                         if (this.lastInsertClass != targetElClass){
47623                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47624                                 this.lastInsertClass = targetElClass;
47625                         }
47626                 }
47627                 return dragElClass;
47628         },
47629
47630     onNodeOut : function(n, dd, e, data){
47631                 this.removeDropIndicators(n);
47632     },
47633
47634     onNodeDrop : function(n, dd, e, data){
47635         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47636                 return false;
47637         }
47638         var pt = this.getDropPoint(e, n, dd);
47639                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47640                 if (pt == "below") { insertAt++; }
47641                 for (var i = 0; i < data.records.length; i++) {
47642                         var r = data.records[i];
47643                         var dup = this.store.getById(r.id);
47644                         if (dup && (dd != this.dragZone)) {
47645                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47646                         } else {
47647                                 if (data.copy) {
47648                                         this.store.insert(insertAt++, r.copy());
47649                                 } else {
47650                                         data.source.isDirtyFlag = true;
47651                                         r.store.remove(r);
47652                                         this.store.insert(insertAt++, r);
47653                                 }
47654                                 this.isDirtyFlag = true;
47655                         }
47656                 }
47657                 this.dragZone.cachedTarget = null;
47658                 return true;
47659     },
47660
47661     removeDropIndicators : function(n){
47662                 if(n){
47663                         Roo.fly(n).removeClass([
47664                                 "x-view-drag-insert-above",
47665                                 "x-view-drag-insert-below"]);
47666                         this.lastInsertClass = "_noclass";
47667                 }
47668     },
47669
47670 /**
47671  *      Utility method. Add a delete option to the DDView's context menu.
47672  *      @param {String} imageUrl The URL of the "delete" icon image.
47673  */
47674         setDeletable: function(imageUrl) {
47675                 if (!this.singleSelect && !this.multiSelect) {
47676                         this.singleSelect = true;
47677                 }
47678                 var c = this.getContextMenu();
47679                 this.contextMenu.on("itemclick", function(item) {
47680                         switch (item.id) {
47681                                 case "delete":
47682                                         this.remove(this.getSelectedIndexes());
47683                                         break;
47684                         }
47685                 }, this);
47686                 this.contextMenu.add({
47687                         icon: imageUrl,
47688                         id: "delete",
47689                         text: 'Delete'
47690                 });
47691         },
47692         
47693 /**     Return the context menu for this DDView. */
47694         getContextMenu: function() {
47695                 if (!this.contextMenu) {
47696 //                      Create the View's context menu
47697                         this.contextMenu = new Roo.menu.Menu({
47698                                 id: this.id + "-contextmenu"
47699                         });
47700                         this.el.on("contextmenu", this.showContextMenu, this);
47701                 }
47702                 return this.contextMenu;
47703         },
47704         
47705         disableContextMenu: function() {
47706                 if (this.contextMenu) {
47707                         this.el.un("contextmenu", this.showContextMenu, this);
47708                 }
47709         },
47710
47711         showContextMenu: function(e, item) {
47712         item = this.findItemFromChild(e.getTarget());
47713                 if (item) {
47714                         e.stopEvent();
47715                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47716                         this.contextMenu.showAt(e.getXY());
47717             }
47718     },
47719
47720 /**
47721  *      Remove {@link Roo.data.Record}s at the specified indices.
47722  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47723  */
47724     remove: function(selectedIndices) {
47725                 selectedIndices = [].concat(selectedIndices);
47726                 for (var i = 0; i < selectedIndices.length; i++) {
47727                         var rec = this.store.getAt(selectedIndices[i]);
47728                         this.store.remove(rec);
47729                 }
47730     },
47731
47732 /**
47733  *      Double click fires the event, but also, if this is draggable, and there is only one other
47734  *      related DropZone, it transfers the selected node.
47735  */
47736     onDblClick : function(e){
47737         var item = this.findItemFromChild(e.getTarget());
47738         if(item){
47739             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47740                 return false;
47741             }
47742             if (this.dragGroup) {
47743                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47744                     while (targets.indexOf(this.dropZone) > -1) {
47745                             targets.remove(this.dropZone);
47746                                 }
47747                     if (targets.length == 1) {
47748                                         this.dragZone.cachedTarget = null;
47749                         var el = Roo.get(targets[0].getEl());
47750                         var box = el.getBox(true);
47751                         targets[0].onNodeDrop(el.dom, {
47752                                 target: el.dom,
47753                                 xy: [box.x, box.y + box.height - 1]
47754                         }, null, this.getDragData(e));
47755                     }
47756                 }
47757         }
47758     },
47759     
47760     handleSelection: function(e) {
47761                 this.dragZone.cachedTarget = null;
47762         var item = this.findItemFromChild(e.getTarget());
47763         if (!item) {
47764                 this.clearSelections(true);
47765                 return;
47766         }
47767                 if (item && (this.multiSelect || this.singleSelect)){
47768                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47769                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47770                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47771                                 this.unselect(item);
47772                         } else {
47773                                 this.select(item, this.multiSelect && e.ctrlKey);
47774                                 this.lastSelection = item;
47775                         }
47776                 }
47777     },
47778
47779     onItemClick : function(item, index, e){
47780                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47781                         return false;
47782                 }
47783                 return true;
47784     },
47785
47786     unselect : function(nodeInfo, suppressEvent){
47787                 var node = this.getNode(nodeInfo);
47788                 if(node && this.isSelected(node)){
47789                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47790                                 Roo.fly(node).removeClass(this.selectedClass);
47791                                 this.selections.remove(node);
47792                                 if(!suppressEvent){
47793                                         this.fireEvent("selectionchange", this, this.selections);
47794                                 }
47795                         }
47796                 }
47797     }
47798 });
47799 /*
47800  * Based on:
47801  * Ext JS Library 1.1.1
47802  * Copyright(c) 2006-2007, Ext JS, LLC.
47803  *
47804  * Originally Released Under LGPL - original licence link has changed is not relivant.
47805  *
47806  * Fork - LGPL
47807  * <script type="text/javascript">
47808  */
47809  
47810 /**
47811  * @class Roo.LayoutManager
47812  * @extends Roo.util.Observable
47813  * Base class for layout managers.
47814  */
47815 Roo.LayoutManager = function(container, config){
47816     Roo.LayoutManager.superclass.constructor.call(this);
47817     this.el = Roo.get(container);
47818     // ie scrollbar fix
47819     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47820         document.body.scroll = "no";
47821     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47822         this.el.position('relative');
47823     }
47824     this.id = this.el.id;
47825     this.el.addClass("x-layout-container");
47826     /** false to disable window resize monitoring @type Boolean */
47827     this.monitorWindowResize = true;
47828     this.regions = {};
47829     this.addEvents({
47830         /**
47831          * @event layout
47832          * Fires when a layout is performed. 
47833          * @param {Roo.LayoutManager} this
47834          */
47835         "layout" : true,
47836         /**
47837          * @event regionresized
47838          * Fires when the user resizes a region. 
47839          * @param {Roo.LayoutRegion} region The resized region
47840          * @param {Number} newSize The new size (width for east/west, height for north/south)
47841          */
47842         "regionresized" : true,
47843         /**
47844          * @event regioncollapsed
47845          * Fires when a region is collapsed. 
47846          * @param {Roo.LayoutRegion} region The collapsed region
47847          */
47848         "regioncollapsed" : true,
47849         /**
47850          * @event regionexpanded
47851          * Fires when a region is expanded.  
47852          * @param {Roo.LayoutRegion} region The expanded region
47853          */
47854         "regionexpanded" : true
47855     });
47856     this.updating = false;
47857     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47858 };
47859
47860 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47861     /**
47862      * Returns true if this layout is currently being updated
47863      * @return {Boolean}
47864      */
47865     isUpdating : function(){
47866         return this.updating; 
47867     },
47868     
47869     /**
47870      * Suspend the LayoutManager from doing auto-layouts while
47871      * making multiple add or remove calls
47872      */
47873     beginUpdate : function(){
47874         this.updating = true;    
47875     },
47876     
47877     /**
47878      * Restore auto-layouts and optionally disable the manager from performing a layout
47879      * @param {Boolean} noLayout true to disable a layout update 
47880      */
47881     endUpdate : function(noLayout){
47882         this.updating = false;
47883         if(!noLayout){
47884             this.layout();
47885         }    
47886     },
47887     
47888     layout: function(){
47889         
47890     },
47891     
47892     onRegionResized : function(region, newSize){
47893         this.fireEvent("regionresized", region, newSize);
47894         this.layout();
47895     },
47896     
47897     onRegionCollapsed : function(region){
47898         this.fireEvent("regioncollapsed", region);
47899     },
47900     
47901     onRegionExpanded : function(region){
47902         this.fireEvent("regionexpanded", region);
47903     },
47904         
47905     /**
47906      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47907      * performs box-model adjustments.
47908      * @return {Object} The size as an object {width: (the width), height: (the height)}
47909      */
47910     getViewSize : function(){
47911         var size;
47912         if(this.el.dom != document.body){
47913             size = this.el.getSize();
47914         }else{
47915             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47916         }
47917         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47918         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47919         return size;
47920     },
47921     
47922     /**
47923      * Returns the Element this layout is bound to.
47924      * @return {Roo.Element}
47925      */
47926     getEl : function(){
47927         return this.el;
47928     },
47929     
47930     /**
47931      * Returns the specified region.
47932      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47933      * @return {Roo.LayoutRegion}
47934      */
47935     getRegion : function(target){
47936         return this.regions[target.toLowerCase()];
47937     },
47938     
47939     onWindowResize : function(){
47940         if(this.monitorWindowResize){
47941             this.layout();
47942         }
47943     }
47944 });/*
47945  * Based on:
47946  * Ext JS Library 1.1.1
47947  * Copyright(c) 2006-2007, Ext JS, LLC.
47948  *
47949  * Originally Released Under LGPL - original licence link has changed is not relivant.
47950  *
47951  * Fork - LGPL
47952  * <script type="text/javascript">
47953  */
47954 /**
47955  * @class Roo.BorderLayout
47956  * @extends Roo.LayoutManager
47957  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47958  * please see: <br><br>
47959  * <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>
47960  * <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>
47961  * Example:
47962  <pre><code>
47963  var layout = new Roo.BorderLayout(document.body, {
47964     north: {
47965         initialSize: 25,
47966         titlebar: false
47967     },
47968     west: {
47969         split:true,
47970         initialSize: 200,
47971         minSize: 175,
47972         maxSize: 400,
47973         titlebar: true,
47974         collapsible: true
47975     },
47976     east: {
47977         split:true,
47978         initialSize: 202,
47979         minSize: 175,
47980         maxSize: 400,
47981         titlebar: true,
47982         collapsible: true
47983     },
47984     south: {
47985         split:true,
47986         initialSize: 100,
47987         minSize: 100,
47988         maxSize: 200,
47989         titlebar: true,
47990         collapsible: true
47991     },
47992     center: {
47993         titlebar: true,
47994         autoScroll:true,
47995         resizeTabs: true,
47996         minTabWidth: 50,
47997         preferredTabWidth: 150
47998     }
47999 });
48000
48001 // shorthand
48002 var CP = Roo.ContentPanel;
48003
48004 layout.beginUpdate();
48005 layout.add("north", new CP("north", "North"));
48006 layout.add("south", new CP("south", {title: "South", closable: true}));
48007 layout.add("west", new CP("west", {title: "West"}));
48008 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48009 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48010 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48011 layout.getRegion("center").showPanel("center1");
48012 layout.endUpdate();
48013 </code></pre>
48014
48015 <b>The container the layout is rendered into can be either the body element or any other element.
48016 If it is not the body element, the container needs to either be an absolute positioned element,
48017 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48018 the container size if it is not the body element.</b>
48019
48020 * @constructor
48021 * Create a new BorderLayout
48022 * @param {String/HTMLElement/Element} container The container this layout is bound to
48023 * @param {Object} config Configuration options
48024  */
48025 Roo.BorderLayout = function(container, config){
48026     config = config || {};
48027     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48028     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48029     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48030         var target = this.factory.validRegions[i];
48031         if(config[target]){
48032             this.addRegion(target, config[target]);
48033         }
48034     }
48035 };
48036
48037 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48038     /**
48039      * Creates and adds a new region if it doesn't already exist.
48040      * @param {String} target The target region key (north, south, east, west or center).
48041      * @param {Object} config The regions config object
48042      * @return {BorderLayoutRegion} The new region
48043      */
48044     addRegion : function(target, config){
48045         if(!this.regions[target]){
48046             var r = this.factory.create(target, this, config);
48047             this.bindRegion(target, r);
48048         }
48049         return this.regions[target];
48050     },
48051
48052     // private (kinda)
48053     bindRegion : function(name, r){
48054         this.regions[name] = r;
48055         r.on("visibilitychange", this.layout, this);
48056         r.on("paneladded", this.layout, this);
48057         r.on("panelremoved", this.layout, this);
48058         r.on("invalidated", this.layout, this);
48059         r.on("resized", this.onRegionResized, this);
48060         r.on("collapsed", this.onRegionCollapsed, this);
48061         r.on("expanded", this.onRegionExpanded, this);
48062     },
48063
48064     /**
48065      * Performs a layout update.
48066      */
48067     layout : function(){
48068         if(this.updating) return;
48069         var size = this.getViewSize();
48070         var w = size.width;
48071         var h = size.height;
48072         var centerW = w;
48073         var centerH = h;
48074         var centerY = 0;
48075         var centerX = 0;
48076         //var x = 0, y = 0;
48077
48078         var rs = this.regions;
48079         var north = rs["north"];
48080         var south = rs["south"]; 
48081         var west = rs["west"];
48082         var east = rs["east"];
48083         var center = rs["center"];
48084         //if(this.hideOnLayout){ // not supported anymore
48085             //c.el.setStyle("display", "none");
48086         //}
48087         if(north && north.isVisible()){
48088             var b = north.getBox();
48089             var m = north.getMargins();
48090             b.width = w - (m.left+m.right);
48091             b.x = m.left;
48092             b.y = m.top;
48093             centerY = b.height + b.y + m.bottom;
48094             centerH -= centerY;
48095             north.updateBox(this.safeBox(b));
48096         }
48097         if(south && south.isVisible()){
48098             var b = south.getBox();
48099             var m = south.getMargins();
48100             b.width = w - (m.left+m.right);
48101             b.x = m.left;
48102             var totalHeight = (b.height + m.top + m.bottom);
48103             b.y = h - totalHeight + m.top;
48104             centerH -= totalHeight;
48105             south.updateBox(this.safeBox(b));
48106         }
48107         if(west && west.isVisible()){
48108             var b = west.getBox();
48109             var m = west.getMargins();
48110             b.height = centerH - (m.top+m.bottom);
48111             b.x = m.left;
48112             b.y = centerY + m.top;
48113             var totalWidth = (b.width + m.left + m.right);
48114             centerX += totalWidth;
48115             centerW -= totalWidth;
48116             west.updateBox(this.safeBox(b));
48117         }
48118         if(east && east.isVisible()){
48119             var b = east.getBox();
48120             var m = east.getMargins();
48121             b.height = centerH - (m.top+m.bottom);
48122             var totalWidth = (b.width + m.left + m.right);
48123             b.x = w - totalWidth + m.left;
48124             b.y = centerY + m.top;
48125             centerW -= totalWidth;
48126             east.updateBox(this.safeBox(b));
48127         }
48128         if(center){
48129             var m = center.getMargins();
48130             var centerBox = {
48131                 x: centerX + m.left,
48132                 y: centerY + m.top,
48133                 width: centerW - (m.left+m.right),
48134                 height: centerH - (m.top+m.bottom)
48135             };
48136             //if(this.hideOnLayout){
48137                 //center.el.setStyle("display", "block");
48138             //}
48139             center.updateBox(this.safeBox(centerBox));
48140         }
48141         this.el.repaint();
48142         this.fireEvent("layout", this);
48143     },
48144
48145     // private
48146     safeBox : function(box){
48147         box.width = Math.max(0, box.width);
48148         box.height = Math.max(0, box.height);
48149         return box;
48150     },
48151
48152     /**
48153      * Adds a ContentPanel (or subclass) to this layout.
48154      * @param {String} target The target region key (north, south, east, west or center).
48155      * @param {Roo.ContentPanel} panel The panel to add
48156      * @return {Roo.ContentPanel} The added panel
48157      */
48158     add : function(target, panel){
48159          
48160         target = target.toLowerCase();
48161         return this.regions[target].add(panel);
48162     },
48163
48164     /**
48165      * Remove a ContentPanel (or subclass) to this layout.
48166      * @param {String} target The target region key (north, south, east, west or center).
48167      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48168      * @return {Roo.ContentPanel} The removed panel
48169      */
48170     remove : function(target, panel){
48171         target = target.toLowerCase();
48172         return this.regions[target].remove(panel);
48173     },
48174
48175     /**
48176      * Searches all regions for a panel with the specified id
48177      * @param {String} panelId
48178      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48179      */
48180     findPanel : function(panelId){
48181         var rs = this.regions;
48182         for(var target in rs){
48183             if(typeof rs[target] != "function"){
48184                 var p = rs[target].getPanel(panelId);
48185                 if(p){
48186                     return p;
48187                 }
48188             }
48189         }
48190         return null;
48191     },
48192
48193     /**
48194      * Searches all regions for a panel with the specified id and activates (shows) it.
48195      * @param {String/ContentPanel} panelId The panels id or the panel itself
48196      * @return {Roo.ContentPanel} The shown panel or null
48197      */
48198     showPanel : function(panelId) {
48199       var rs = this.regions;
48200       for(var target in rs){
48201          var r = rs[target];
48202          if(typeof r != "function"){
48203             if(r.hasPanel(panelId)){
48204                return r.showPanel(panelId);
48205             }
48206          }
48207       }
48208       return null;
48209    },
48210
48211    /**
48212      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48213      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48214      */
48215     restoreState : function(provider){
48216         if(!provider){
48217             provider = Roo.state.Manager;
48218         }
48219         var sm = new Roo.LayoutStateManager();
48220         sm.init(this, provider);
48221     },
48222
48223     /**
48224      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48225      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48226      * a valid ContentPanel config object.  Example:
48227      * <pre><code>
48228 // Create the main layout
48229 var layout = new Roo.BorderLayout('main-ct', {
48230     west: {
48231         split:true,
48232         minSize: 175,
48233         titlebar: true
48234     },
48235     center: {
48236         title:'Components'
48237     }
48238 }, 'main-ct');
48239
48240 // Create and add multiple ContentPanels at once via configs
48241 layout.batchAdd({
48242    west: {
48243        id: 'source-files',
48244        autoCreate:true,
48245        title:'Ext Source Files',
48246        autoScroll:true,
48247        fitToFrame:true
48248    },
48249    center : {
48250        el: cview,
48251        autoScroll:true,
48252        fitToFrame:true,
48253        toolbar: tb,
48254        resizeEl:'cbody'
48255    }
48256 });
48257 </code></pre>
48258      * @param {Object} regions An object containing ContentPanel configs by region name
48259      */
48260     batchAdd : function(regions){
48261         this.beginUpdate();
48262         for(var rname in regions){
48263             var lr = this.regions[rname];
48264             if(lr){
48265                 this.addTypedPanels(lr, regions[rname]);
48266             }
48267         }
48268         this.endUpdate();
48269     },
48270
48271     // private
48272     addTypedPanels : function(lr, ps){
48273         if(typeof ps == 'string'){
48274             lr.add(new Roo.ContentPanel(ps));
48275         }
48276         else if(ps instanceof Array){
48277             for(var i =0, len = ps.length; i < len; i++){
48278                 this.addTypedPanels(lr, ps[i]);
48279             }
48280         }
48281         else if(!ps.events){ // raw config?
48282             var el = ps.el;
48283             delete ps.el; // prevent conflict
48284             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48285         }
48286         else {  // panel object assumed!
48287             lr.add(ps);
48288         }
48289     },
48290     /**
48291      * Adds a xtype elements to the layout.
48292      * <pre><code>
48293
48294 layout.addxtype({
48295        xtype : 'ContentPanel',
48296        region: 'west',
48297        items: [ .... ]
48298    }
48299 );
48300
48301 layout.addxtype({
48302         xtype : 'NestedLayoutPanel',
48303         region: 'west',
48304         layout: {
48305            center: { },
48306            west: { }   
48307         },
48308         items : [ ... list of content panels or nested layout panels.. ]
48309    }
48310 );
48311 </code></pre>
48312      * @param {Object} cfg Xtype definition of item to add.
48313      */
48314     addxtype : function(cfg)
48315     {
48316         // basically accepts a pannel...
48317         // can accept a layout region..!?!?
48318         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48319         
48320         if (!cfg.xtype.match(/Panel$/)) {
48321             return false;
48322         }
48323         var ret = false;
48324         
48325         if (typeof(cfg.region) == 'undefined') {
48326             Roo.log("Failed to add Panel, region was not set");
48327             Roo.log(cfg);
48328             return false;
48329         }
48330         var region = cfg.region;
48331         delete cfg.region;
48332         
48333           
48334         var xitems = [];
48335         if (cfg.items) {
48336             xitems = cfg.items;
48337             delete cfg.items;
48338         }
48339         var nb = false;
48340         
48341         switch(cfg.xtype) 
48342         {
48343             case 'ContentPanel':  // ContentPanel (el, cfg)
48344             case 'ScrollPanel':  // ContentPanel (el, cfg)
48345             case 'ViewPanel': 
48346                 if(cfg.autoCreate) {
48347                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48348                 } else {
48349                     var el = this.el.createChild();
48350                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48351                 }
48352                 
48353                 this.add(region, ret);
48354                 break;
48355             
48356             
48357             case 'TreePanel': // our new panel!
48358                 cfg.el = this.el.createChild();
48359                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48360                 this.add(region, ret);
48361                 break;
48362             
48363             case 'NestedLayoutPanel': 
48364                 // create a new Layout (which is  a Border Layout...
48365                 var el = this.el.createChild();
48366                 var clayout = cfg.layout;
48367                 delete cfg.layout;
48368                 clayout.items   = clayout.items  || [];
48369                 // replace this exitems with the clayout ones..
48370                 xitems = clayout.items;
48371                  
48372                 
48373                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48374                     cfg.background = false;
48375                 }
48376                 var layout = new Roo.BorderLayout(el, clayout);
48377                 
48378                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48379                 //console.log('adding nested layout panel '  + cfg.toSource());
48380                 this.add(region, ret);
48381                 nb = {}; /// find first...
48382                 break;
48383                 
48384             case 'GridPanel': 
48385             
48386                 // needs grid and region
48387                 
48388                 //var el = this.getRegion(region).el.createChild();
48389                 var el = this.el.createChild();
48390                 // create the grid first...
48391                 
48392                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48393                 delete cfg.grid;
48394                 if (region == 'center' && this.active ) {
48395                     cfg.background = false;
48396                 }
48397                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48398                 
48399                 this.add(region, ret);
48400                 if (cfg.background) {
48401                     ret.on('activate', function(gp) {
48402                         if (!gp.grid.rendered) {
48403                             gp.grid.render();
48404                         }
48405                     });
48406                 } else {
48407                     grid.render();
48408                 }
48409                 break;
48410            
48411            
48412            
48413                 
48414                 
48415                 
48416             default:
48417                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
48418                     
48419                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48420                     this.add(region, ret);
48421                 } else {
48422                 
48423                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48424                     return null;
48425                 }
48426                 
48427              // GridPanel (grid, cfg)
48428             
48429         }
48430         this.beginUpdate();
48431         // add children..
48432         var region = '';
48433         var abn = {};
48434         Roo.each(xitems, function(i)  {
48435             region = nb && i.region ? i.region : false;
48436             
48437             var add = ret.addxtype(i);
48438            
48439             if (region) {
48440                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48441                 if (!i.background) {
48442                     abn[region] = nb[region] ;
48443                 }
48444             }
48445             
48446         });
48447         this.endUpdate();
48448
48449         // make the last non-background panel active..
48450         //if (nb) { Roo.log(abn); }
48451         if (nb) {
48452             
48453             for(var r in abn) {
48454                 region = this.getRegion(r);
48455                 if (region) {
48456                     // tried using nb[r], but it does not work..
48457                      
48458                     region.showPanel(abn[r]);
48459                    
48460                 }
48461             }
48462         }
48463         return ret;
48464         
48465     }
48466 });
48467
48468 /**
48469  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48470  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48471  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48472  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48473  * <pre><code>
48474 // shorthand
48475 var CP = Roo.ContentPanel;
48476
48477 var layout = Roo.BorderLayout.create({
48478     north: {
48479         initialSize: 25,
48480         titlebar: false,
48481         panels: [new CP("north", "North")]
48482     },
48483     west: {
48484         split:true,
48485         initialSize: 200,
48486         minSize: 175,
48487         maxSize: 400,
48488         titlebar: true,
48489         collapsible: true,
48490         panels: [new CP("west", {title: "West"})]
48491     },
48492     east: {
48493         split:true,
48494         initialSize: 202,
48495         minSize: 175,
48496         maxSize: 400,
48497         titlebar: true,
48498         collapsible: true,
48499         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48500     },
48501     south: {
48502         split:true,
48503         initialSize: 100,
48504         minSize: 100,
48505         maxSize: 200,
48506         titlebar: true,
48507         collapsible: true,
48508         panels: [new CP("south", {title: "South", closable: true})]
48509     },
48510     center: {
48511         titlebar: true,
48512         autoScroll:true,
48513         resizeTabs: true,
48514         minTabWidth: 50,
48515         preferredTabWidth: 150,
48516         panels: [
48517             new CP("center1", {title: "Close Me", closable: true}),
48518             new CP("center2", {title: "Center Panel", closable: false})
48519         ]
48520     }
48521 }, document.body);
48522
48523 layout.getRegion("center").showPanel("center1");
48524 </code></pre>
48525  * @param config
48526  * @param targetEl
48527  */
48528 Roo.BorderLayout.create = function(config, targetEl){
48529     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48530     layout.beginUpdate();
48531     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48532     for(var j = 0, jlen = regions.length; j < jlen; j++){
48533         var lr = regions[j];
48534         if(layout.regions[lr] && config[lr].panels){
48535             var r = layout.regions[lr];
48536             var ps = config[lr].panels;
48537             layout.addTypedPanels(r, ps);
48538         }
48539     }
48540     layout.endUpdate();
48541     return layout;
48542 };
48543
48544 // private
48545 Roo.BorderLayout.RegionFactory = {
48546     // private
48547     validRegions : ["north","south","east","west","center"],
48548
48549     // private
48550     create : function(target, mgr, config){
48551         target = target.toLowerCase();
48552         if(config.lightweight || config.basic){
48553             return new Roo.BasicLayoutRegion(mgr, config, target);
48554         }
48555         switch(target){
48556             case "north":
48557                 return new Roo.NorthLayoutRegion(mgr, config);
48558             case "south":
48559                 return new Roo.SouthLayoutRegion(mgr, config);
48560             case "east":
48561                 return new Roo.EastLayoutRegion(mgr, config);
48562             case "west":
48563                 return new Roo.WestLayoutRegion(mgr, config);
48564             case "center":
48565                 return new Roo.CenterLayoutRegion(mgr, config);
48566         }
48567         throw 'Layout region "'+target+'" not supported.';
48568     }
48569 };/*
48570  * Based on:
48571  * Ext JS Library 1.1.1
48572  * Copyright(c) 2006-2007, Ext JS, LLC.
48573  *
48574  * Originally Released Under LGPL - original licence link has changed is not relivant.
48575  *
48576  * Fork - LGPL
48577  * <script type="text/javascript">
48578  */
48579  
48580 /**
48581  * @class Roo.BasicLayoutRegion
48582  * @extends Roo.util.Observable
48583  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48584  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48585  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48586  */
48587 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48588     this.mgr = mgr;
48589     this.position  = pos;
48590     this.events = {
48591         /**
48592          * @scope Roo.BasicLayoutRegion
48593          */
48594         
48595         /**
48596          * @event beforeremove
48597          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48598          * @param {Roo.LayoutRegion} this
48599          * @param {Roo.ContentPanel} panel The panel
48600          * @param {Object} e The cancel event object
48601          */
48602         "beforeremove" : true,
48603         /**
48604          * @event invalidated
48605          * Fires when the layout for this region is changed.
48606          * @param {Roo.LayoutRegion} this
48607          */
48608         "invalidated" : true,
48609         /**
48610          * @event visibilitychange
48611          * Fires when this region is shown or hidden 
48612          * @param {Roo.LayoutRegion} this
48613          * @param {Boolean} visibility true or false
48614          */
48615         "visibilitychange" : true,
48616         /**
48617          * @event paneladded
48618          * Fires when a panel is added. 
48619          * @param {Roo.LayoutRegion} this
48620          * @param {Roo.ContentPanel} panel The panel
48621          */
48622         "paneladded" : true,
48623         /**
48624          * @event panelremoved
48625          * Fires when a panel is removed. 
48626          * @param {Roo.LayoutRegion} this
48627          * @param {Roo.ContentPanel} panel The panel
48628          */
48629         "panelremoved" : true,
48630         /**
48631          * @event collapsed
48632          * Fires when this region is collapsed.
48633          * @param {Roo.LayoutRegion} this
48634          */
48635         "collapsed" : true,
48636         /**
48637          * @event expanded
48638          * Fires when this region is expanded.
48639          * @param {Roo.LayoutRegion} this
48640          */
48641         "expanded" : true,
48642         /**
48643          * @event slideshow
48644          * Fires when this region is slid into view.
48645          * @param {Roo.LayoutRegion} this
48646          */
48647         "slideshow" : true,
48648         /**
48649          * @event slidehide
48650          * Fires when this region slides out of view. 
48651          * @param {Roo.LayoutRegion} this
48652          */
48653         "slidehide" : true,
48654         /**
48655          * @event panelactivated
48656          * Fires when a panel is activated. 
48657          * @param {Roo.LayoutRegion} this
48658          * @param {Roo.ContentPanel} panel The activated panel
48659          */
48660         "panelactivated" : true,
48661         /**
48662          * @event resized
48663          * Fires when the user resizes this region. 
48664          * @param {Roo.LayoutRegion} this
48665          * @param {Number} newSize The new size (width for east/west, height for north/south)
48666          */
48667         "resized" : true
48668     };
48669     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48670     this.panels = new Roo.util.MixedCollection();
48671     this.panels.getKey = this.getPanelId.createDelegate(this);
48672     this.box = null;
48673     this.activePanel = null;
48674     // ensure listeners are added...
48675     
48676     if (config.listeners || config.events) {
48677         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48678             listeners : config.listeners || {},
48679             events : config.events || {}
48680         });
48681     }
48682     
48683     if(skipConfig !== true){
48684         this.applyConfig(config);
48685     }
48686 };
48687
48688 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48689     getPanelId : function(p){
48690         return p.getId();
48691     },
48692     
48693     applyConfig : function(config){
48694         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48695         this.config = config;
48696         
48697     },
48698     
48699     /**
48700      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48701      * the width, for horizontal (north, south) the height.
48702      * @param {Number} newSize The new width or height
48703      */
48704     resizeTo : function(newSize){
48705         var el = this.el ? this.el :
48706                  (this.activePanel ? this.activePanel.getEl() : null);
48707         if(el){
48708             switch(this.position){
48709                 case "east":
48710                 case "west":
48711                     el.setWidth(newSize);
48712                     this.fireEvent("resized", this, newSize);
48713                 break;
48714                 case "north":
48715                 case "south":
48716                     el.setHeight(newSize);
48717                     this.fireEvent("resized", this, newSize);
48718                 break;                
48719             }
48720         }
48721     },
48722     
48723     getBox : function(){
48724         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48725     },
48726     
48727     getMargins : function(){
48728         return this.margins;
48729     },
48730     
48731     updateBox : function(box){
48732         this.box = box;
48733         var el = this.activePanel.getEl();
48734         el.dom.style.left = box.x + "px";
48735         el.dom.style.top = box.y + "px";
48736         this.activePanel.setSize(box.width, box.height);
48737     },
48738     
48739     /**
48740      * Returns the container element for this region.
48741      * @return {Roo.Element}
48742      */
48743     getEl : function(){
48744         return this.activePanel;
48745     },
48746     
48747     /**
48748      * Returns true if this region is currently visible.
48749      * @return {Boolean}
48750      */
48751     isVisible : function(){
48752         return this.activePanel ? true : false;
48753     },
48754     
48755     setActivePanel : function(panel){
48756         panel = this.getPanel(panel);
48757         if(this.activePanel && this.activePanel != panel){
48758             this.activePanel.setActiveState(false);
48759             this.activePanel.getEl().setLeftTop(-10000,-10000);
48760         }
48761         this.activePanel = panel;
48762         panel.setActiveState(true);
48763         if(this.box){
48764             panel.setSize(this.box.width, this.box.height);
48765         }
48766         this.fireEvent("panelactivated", this, panel);
48767         this.fireEvent("invalidated");
48768     },
48769     
48770     /**
48771      * Show the specified panel.
48772      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48773      * @return {Roo.ContentPanel} The shown panel or null
48774      */
48775     showPanel : function(panel){
48776         if(panel = this.getPanel(panel)){
48777             this.setActivePanel(panel);
48778         }
48779         return panel;
48780     },
48781     
48782     /**
48783      * Get the active panel for this region.
48784      * @return {Roo.ContentPanel} The active panel or null
48785      */
48786     getActivePanel : function(){
48787         return this.activePanel;
48788     },
48789     
48790     /**
48791      * Add the passed ContentPanel(s)
48792      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48793      * @return {Roo.ContentPanel} The panel added (if only one was added)
48794      */
48795     add : function(panel){
48796         if(arguments.length > 1){
48797             for(var i = 0, len = arguments.length; i < len; i++) {
48798                 this.add(arguments[i]);
48799             }
48800             return null;
48801         }
48802         if(this.hasPanel(panel)){
48803             this.showPanel(panel);
48804             return panel;
48805         }
48806         var el = panel.getEl();
48807         if(el.dom.parentNode != this.mgr.el.dom){
48808             this.mgr.el.dom.appendChild(el.dom);
48809         }
48810         if(panel.setRegion){
48811             panel.setRegion(this);
48812         }
48813         this.panels.add(panel);
48814         el.setStyle("position", "absolute");
48815         if(!panel.background){
48816             this.setActivePanel(panel);
48817             if(this.config.initialSize && this.panels.getCount()==1){
48818                 this.resizeTo(this.config.initialSize);
48819             }
48820         }
48821         this.fireEvent("paneladded", this, panel);
48822         return panel;
48823     },
48824     
48825     /**
48826      * Returns true if the panel is in this region.
48827      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48828      * @return {Boolean}
48829      */
48830     hasPanel : function(panel){
48831         if(typeof panel == "object"){ // must be panel obj
48832             panel = panel.getId();
48833         }
48834         return this.getPanel(panel) ? true : false;
48835     },
48836     
48837     /**
48838      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48839      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48840      * @param {Boolean} preservePanel Overrides the config preservePanel option
48841      * @return {Roo.ContentPanel} The panel that was removed
48842      */
48843     remove : function(panel, preservePanel){
48844         panel = this.getPanel(panel);
48845         if(!panel){
48846             return null;
48847         }
48848         var e = {};
48849         this.fireEvent("beforeremove", this, panel, e);
48850         if(e.cancel === true){
48851             return null;
48852         }
48853         var panelId = panel.getId();
48854         this.panels.removeKey(panelId);
48855         return panel;
48856     },
48857     
48858     /**
48859      * Returns the panel specified or null if it's not in this region.
48860      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48861      * @return {Roo.ContentPanel}
48862      */
48863     getPanel : function(id){
48864         if(typeof id == "object"){ // must be panel obj
48865             return id;
48866         }
48867         return this.panels.get(id);
48868     },
48869     
48870     /**
48871      * Returns this regions position (north/south/east/west/center).
48872      * @return {String} 
48873      */
48874     getPosition: function(){
48875         return this.position;    
48876     }
48877 });/*
48878  * Based on:
48879  * Ext JS Library 1.1.1
48880  * Copyright(c) 2006-2007, Ext JS, LLC.
48881  *
48882  * Originally Released Under LGPL - original licence link has changed is not relivant.
48883  *
48884  * Fork - LGPL
48885  * <script type="text/javascript">
48886  */
48887  
48888 /**
48889  * @class Roo.LayoutRegion
48890  * @extends Roo.BasicLayoutRegion
48891  * This class represents a region in a layout manager.
48892  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48893  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48894  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48895  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48896  * @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})
48897  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48898  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48899  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48900  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48901  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48902  * @cfg {String}    title           The title for the region (overrides panel titles)
48903  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48904  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48905  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48906  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48907  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48908  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48909  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48910  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48911  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48912  * @cfg {Boolean}   showPin         True to show a pin button
48913  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48914  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48915  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48916  * @cfg {Number}    width           For East/West panels
48917  * @cfg {Number}    height          For North/South panels
48918  * @cfg {Boolean}   split           To show the splitter
48919  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48920  */
48921 Roo.LayoutRegion = function(mgr, config, pos){
48922     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48923     var dh = Roo.DomHelper;
48924     /** This region's container element 
48925     * @type Roo.Element */
48926     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48927     /** This region's title element 
48928     * @type Roo.Element */
48929
48930     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48931         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48932         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48933     ]}, true);
48934     this.titleEl.enableDisplayMode();
48935     /** This region's title text element 
48936     * @type HTMLElement */
48937     this.titleTextEl = this.titleEl.dom.firstChild;
48938     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48939     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48940     this.closeBtn.enableDisplayMode();
48941     this.closeBtn.on("click", this.closeClicked, this);
48942     this.closeBtn.hide();
48943
48944     this.createBody(config);
48945     this.visible = true;
48946     this.collapsed = false;
48947
48948     if(config.hideWhenEmpty){
48949         this.hide();
48950         this.on("paneladded", this.validateVisibility, this);
48951         this.on("panelremoved", this.validateVisibility, this);
48952     }
48953     this.applyConfig(config);
48954 };
48955
48956 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48957
48958     createBody : function(){
48959         /** This region's body element 
48960         * @type Roo.Element */
48961         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48962     },
48963
48964     applyConfig : function(c){
48965         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48966             var dh = Roo.DomHelper;
48967             if(c.titlebar !== false){
48968                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48969                 this.collapseBtn.on("click", this.collapse, this);
48970                 this.collapseBtn.enableDisplayMode();
48971
48972                 if(c.showPin === true || this.showPin){
48973                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48974                     this.stickBtn.enableDisplayMode();
48975                     this.stickBtn.on("click", this.expand, this);
48976                     this.stickBtn.hide();
48977                 }
48978             }
48979             /** This region's collapsed element
48980             * @type Roo.Element */
48981             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48982                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48983             ]}, true);
48984             if(c.floatable !== false){
48985                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48986                this.collapsedEl.on("click", this.collapseClick, this);
48987             }
48988
48989             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48990                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48991                    id: "message", unselectable: "on", style:{"float":"left"}});
48992                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48993              }
48994             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48995             this.expandBtn.on("click", this.expand, this);
48996         }
48997         if(this.collapseBtn){
48998             this.collapseBtn.setVisible(c.collapsible == true);
48999         }
49000         this.cmargins = c.cmargins || this.cmargins ||
49001                          (this.position == "west" || this.position == "east" ?
49002                              {top: 0, left: 2, right:2, bottom: 0} :
49003                              {top: 2, left: 0, right:0, bottom: 2});
49004         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49005         this.bottomTabs = c.tabPosition != "top";
49006         this.autoScroll = c.autoScroll || false;
49007         if(this.autoScroll){
49008             this.bodyEl.setStyle("overflow", "auto");
49009         }else{
49010             this.bodyEl.setStyle("overflow", "hidden");
49011         }
49012         //if(c.titlebar !== false){
49013             if((!c.titlebar && !c.title) || c.titlebar === false){
49014                 this.titleEl.hide();
49015             }else{
49016                 this.titleEl.show();
49017                 if(c.title){
49018                     this.titleTextEl.innerHTML = c.title;
49019                 }
49020             }
49021         //}
49022         this.duration = c.duration || .30;
49023         this.slideDuration = c.slideDuration || .45;
49024         this.config = c;
49025         if(c.collapsed){
49026             this.collapse(true);
49027         }
49028         if(c.hidden){
49029             this.hide();
49030         }
49031     },
49032     /**
49033      * Returns true if this region is currently visible.
49034      * @return {Boolean}
49035      */
49036     isVisible : function(){
49037         return this.visible;
49038     },
49039
49040     /**
49041      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49042      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49043      */
49044     setCollapsedTitle : function(title){
49045         title = title || "&#160;";
49046         if(this.collapsedTitleTextEl){
49047             this.collapsedTitleTextEl.innerHTML = title;
49048         }
49049     },
49050
49051     getBox : function(){
49052         var b;
49053         if(!this.collapsed){
49054             b = this.el.getBox(false, true);
49055         }else{
49056             b = this.collapsedEl.getBox(false, true);
49057         }
49058         return b;
49059     },
49060
49061     getMargins : function(){
49062         return this.collapsed ? this.cmargins : this.margins;
49063     },
49064
49065     highlight : function(){
49066         this.el.addClass("x-layout-panel-dragover");
49067     },
49068
49069     unhighlight : function(){
49070         this.el.removeClass("x-layout-panel-dragover");
49071     },
49072
49073     updateBox : function(box){
49074         this.box = box;
49075         if(!this.collapsed){
49076             this.el.dom.style.left = box.x + "px";
49077             this.el.dom.style.top = box.y + "px";
49078             this.updateBody(box.width, box.height);
49079         }else{
49080             this.collapsedEl.dom.style.left = box.x + "px";
49081             this.collapsedEl.dom.style.top = box.y + "px";
49082             this.collapsedEl.setSize(box.width, box.height);
49083         }
49084         if(this.tabs){
49085             this.tabs.autoSizeTabs();
49086         }
49087     },
49088
49089     updateBody : function(w, h){
49090         if(w !== null){
49091             this.el.setWidth(w);
49092             w -= this.el.getBorderWidth("rl");
49093             if(this.config.adjustments){
49094                 w += this.config.adjustments[0];
49095             }
49096         }
49097         if(h !== null){
49098             this.el.setHeight(h);
49099             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49100             h -= this.el.getBorderWidth("tb");
49101             if(this.config.adjustments){
49102                 h += this.config.adjustments[1];
49103             }
49104             this.bodyEl.setHeight(h);
49105             if(this.tabs){
49106                 h = this.tabs.syncHeight(h);
49107             }
49108         }
49109         if(this.panelSize){
49110             w = w !== null ? w : this.panelSize.width;
49111             h = h !== null ? h : this.panelSize.height;
49112         }
49113         if(this.activePanel){
49114             var el = this.activePanel.getEl();
49115             w = w !== null ? w : el.getWidth();
49116             h = h !== null ? h : el.getHeight();
49117             this.panelSize = {width: w, height: h};
49118             this.activePanel.setSize(w, h);
49119         }
49120         if(Roo.isIE && this.tabs){
49121             this.tabs.el.repaint();
49122         }
49123     },
49124
49125     /**
49126      * Returns the container element for this region.
49127      * @return {Roo.Element}
49128      */
49129     getEl : function(){
49130         return this.el;
49131     },
49132
49133     /**
49134      * Hides this region.
49135      */
49136     hide : function(){
49137         if(!this.collapsed){
49138             this.el.dom.style.left = "-2000px";
49139             this.el.hide();
49140         }else{
49141             this.collapsedEl.dom.style.left = "-2000px";
49142             this.collapsedEl.hide();
49143         }
49144         this.visible = false;
49145         this.fireEvent("visibilitychange", this, false);
49146     },
49147
49148     /**
49149      * Shows this region if it was previously hidden.
49150      */
49151     show : function(){
49152         if(!this.collapsed){
49153             this.el.show();
49154         }else{
49155             this.collapsedEl.show();
49156         }
49157         this.visible = true;
49158         this.fireEvent("visibilitychange", this, true);
49159     },
49160
49161     closeClicked : function(){
49162         if(this.activePanel){
49163             this.remove(this.activePanel);
49164         }
49165     },
49166
49167     collapseClick : function(e){
49168         if(this.isSlid){
49169            e.stopPropagation();
49170            this.slideIn();
49171         }else{
49172            e.stopPropagation();
49173            this.slideOut();
49174         }
49175     },
49176
49177     /**
49178      * Collapses this region.
49179      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49180      */
49181     collapse : function(skipAnim){
49182         if(this.collapsed) return;
49183         this.collapsed = true;
49184         if(this.split){
49185             this.split.el.hide();
49186         }
49187         if(this.config.animate && skipAnim !== true){
49188             this.fireEvent("invalidated", this);
49189             this.animateCollapse();
49190         }else{
49191             this.el.setLocation(-20000,-20000);
49192             this.el.hide();
49193             this.collapsedEl.show();
49194             this.fireEvent("collapsed", this);
49195             this.fireEvent("invalidated", this);
49196         }
49197     },
49198
49199     animateCollapse : function(){
49200         // overridden
49201     },
49202
49203     /**
49204      * Expands this region if it was previously collapsed.
49205      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49206      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49207      */
49208     expand : function(e, skipAnim){
49209         if(e) e.stopPropagation();
49210         if(!this.collapsed || this.el.hasActiveFx()) return;
49211         if(this.isSlid){
49212             this.afterSlideIn();
49213             skipAnim = true;
49214         }
49215         this.collapsed = false;
49216         if(this.config.animate && skipAnim !== true){
49217             this.animateExpand();
49218         }else{
49219             this.el.show();
49220             if(this.split){
49221                 this.split.el.show();
49222             }
49223             this.collapsedEl.setLocation(-2000,-2000);
49224             this.collapsedEl.hide();
49225             this.fireEvent("invalidated", this);
49226             this.fireEvent("expanded", this);
49227         }
49228     },
49229
49230     animateExpand : function(){
49231         // overridden
49232     },
49233
49234     initTabs : function()
49235     {
49236         this.bodyEl.setStyle("overflow", "hidden");
49237         var ts = new Roo.TabPanel(
49238                 this.bodyEl.dom,
49239                 {
49240                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49241                     disableTooltips: this.config.disableTabTips,
49242                     toolbar : this.config.toolbar
49243                 }
49244         );
49245         if(this.config.hideTabs){
49246             ts.stripWrap.setDisplayed(false);
49247         }
49248         this.tabs = ts;
49249         ts.resizeTabs = this.config.resizeTabs === true;
49250         ts.minTabWidth = this.config.minTabWidth || 40;
49251         ts.maxTabWidth = this.config.maxTabWidth || 250;
49252         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49253         ts.monitorResize = false;
49254         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49255         ts.bodyEl.addClass('x-layout-tabs-body');
49256         this.panels.each(this.initPanelAsTab, this);
49257     },
49258
49259     initPanelAsTab : function(panel){
49260         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49261                     this.config.closeOnTab && panel.isClosable());
49262         if(panel.tabTip !== undefined){
49263             ti.setTooltip(panel.tabTip);
49264         }
49265         ti.on("activate", function(){
49266               this.setActivePanel(panel);
49267         }, this);
49268         if(this.config.closeOnTab){
49269             ti.on("beforeclose", function(t, e){
49270                 e.cancel = true;
49271                 this.remove(panel);
49272             }, this);
49273         }
49274         return ti;
49275     },
49276
49277     updatePanelTitle : function(panel, title){
49278         if(this.activePanel == panel){
49279             this.updateTitle(title);
49280         }
49281         if(this.tabs){
49282             var ti = this.tabs.getTab(panel.getEl().id);
49283             ti.setText(title);
49284             if(panel.tabTip !== undefined){
49285                 ti.setTooltip(panel.tabTip);
49286             }
49287         }
49288     },
49289
49290     updateTitle : function(title){
49291         if(this.titleTextEl && !this.config.title){
49292             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49293         }
49294     },
49295
49296     setActivePanel : function(panel){
49297         panel = this.getPanel(panel);
49298         if(this.activePanel && this.activePanel != panel){
49299             this.activePanel.setActiveState(false);
49300         }
49301         this.activePanel = panel;
49302         panel.setActiveState(true);
49303         if(this.panelSize){
49304             panel.setSize(this.panelSize.width, this.panelSize.height);
49305         }
49306         if(this.closeBtn){
49307             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49308         }
49309         this.updateTitle(panel.getTitle());
49310         if(this.tabs){
49311             this.fireEvent("invalidated", this);
49312         }
49313         this.fireEvent("panelactivated", this, panel);
49314     },
49315
49316     /**
49317      * Shows the specified panel.
49318      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49319      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49320      */
49321     showPanel : function(panel){
49322         if(panel = this.getPanel(panel)){
49323             if(this.tabs){
49324                 var tab = this.tabs.getTab(panel.getEl().id);
49325                 if(tab.isHidden()){
49326                     this.tabs.unhideTab(tab.id);
49327                 }
49328                 tab.activate();
49329             }else{
49330                 this.setActivePanel(panel);
49331             }
49332         }
49333         return panel;
49334     },
49335
49336     /**
49337      * Get the active panel for this region.
49338      * @return {Roo.ContentPanel} The active panel or null
49339      */
49340     getActivePanel : function(){
49341         return this.activePanel;
49342     },
49343
49344     validateVisibility : function(){
49345         if(this.panels.getCount() < 1){
49346             this.updateTitle("&#160;");
49347             this.closeBtn.hide();
49348             this.hide();
49349         }else{
49350             if(!this.isVisible()){
49351                 this.show();
49352             }
49353         }
49354     },
49355
49356     /**
49357      * Adds the passed ContentPanel(s) to this region.
49358      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49359      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49360      */
49361     add : function(panel){
49362         if(arguments.length > 1){
49363             for(var i = 0, len = arguments.length; i < len; i++) {
49364                 this.add(arguments[i]);
49365             }
49366             return null;
49367         }
49368         if(this.hasPanel(panel)){
49369             this.showPanel(panel);
49370             return panel;
49371         }
49372         panel.setRegion(this);
49373         this.panels.add(panel);
49374         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49375             this.bodyEl.dom.appendChild(panel.getEl().dom);
49376             if(panel.background !== true){
49377                 this.setActivePanel(panel);
49378             }
49379             this.fireEvent("paneladded", this, panel);
49380             return panel;
49381         }
49382         if(!this.tabs){
49383             this.initTabs();
49384         }else{
49385             this.initPanelAsTab(panel);
49386         }
49387         if(panel.background !== true){
49388             this.tabs.activate(panel.getEl().id);
49389         }
49390         this.fireEvent("paneladded", this, panel);
49391         return panel;
49392     },
49393
49394     /**
49395      * Hides the tab for the specified panel.
49396      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49397      */
49398     hidePanel : function(panel){
49399         if(this.tabs && (panel = this.getPanel(panel))){
49400             this.tabs.hideTab(panel.getEl().id);
49401         }
49402     },
49403
49404     /**
49405      * Unhides the tab for a previously hidden panel.
49406      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49407      */
49408     unhidePanel : function(panel){
49409         if(this.tabs && (panel = this.getPanel(panel))){
49410             this.tabs.unhideTab(panel.getEl().id);
49411         }
49412     },
49413
49414     clearPanels : function(){
49415         while(this.panels.getCount() > 0){
49416              this.remove(this.panels.first());
49417         }
49418     },
49419
49420     /**
49421      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49422      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49423      * @param {Boolean} preservePanel Overrides the config preservePanel option
49424      * @return {Roo.ContentPanel} The panel that was removed
49425      */
49426     remove : function(panel, preservePanel){
49427         panel = this.getPanel(panel);
49428         if(!panel){
49429             return null;
49430         }
49431         var e = {};
49432         this.fireEvent("beforeremove", this, panel, e);
49433         if(e.cancel === true){
49434             return null;
49435         }
49436         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49437         var panelId = panel.getId();
49438         this.panels.removeKey(panelId);
49439         if(preservePanel){
49440             document.body.appendChild(panel.getEl().dom);
49441         }
49442         if(this.tabs){
49443             this.tabs.removeTab(panel.getEl().id);
49444         }else if (!preservePanel){
49445             this.bodyEl.dom.removeChild(panel.getEl().dom);
49446         }
49447         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49448             var p = this.panels.first();
49449             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49450             tempEl.appendChild(p.getEl().dom);
49451             this.bodyEl.update("");
49452             this.bodyEl.dom.appendChild(p.getEl().dom);
49453             tempEl = null;
49454             this.updateTitle(p.getTitle());
49455             this.tabs = null;
49456             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49457             this.setActivePanel(p);
49458         }
49459         panel.setRegion(null);
49460         if(this.activePanel == panel){
49461             this.activePanel = null;
49462         }
49463         if(this.config.autoDestroy !== false && preservePanel !== true){
49464             try{panel.destroy();}catch(e){}
49465         }
49466         this.fireEvent("panelremoved", this, panel);
49467         return panel;
49468     },
49469
49470     /**
49471      * Returns the TabPanel component used by this region
49472      * @return {Roo.TabPanel}
49473      */
49474     getTabs : function(){
49475         return this.tabs;
49476     },
49477
49478     createTool : function(parentEl, className){
49479         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49480             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49481         btn.addClassOnOver("x-layout-tools-button-over");
49482         return btn;
49483     }
49484 });/*
49485  * Based on:
49486  * Ext JS Library 1.1.1
49487  * Copyright(c) 2006-2007, Ext JS, LLC.
49488  *
49489  * Originally Released Under LGPL - original licence link has changed is not relivant.
49490  *
49491  * Fork - LGPL
49492  * <script type="text/javascript">
49493  */
49494  
49495
49496
49497 /**
49498  * @class Roo.SplitLayoutRegion
49499  * @extends Roo.LayoutRegion
49500  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49501  */
49502 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49503     this.cursor = cursor;
49504     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49505 };
49506
49507 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49508     splitTip : "Drag to resize.",
49509     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49510     useSplitTips : false,
49511
49512     applyConfig : function(config){
49513         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49514         if(config.split){
49515             if(!this.split){
49516                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49517                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49518                 /** The SplitBar for this region 
49519                 * @type Roo.SplitBar */
49520                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49521                 this.split.on("moved", this.onSplitMove, this);
49522                 this.split.useShim = config.useShim === true;
49523                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49524                 if(this.useSplitTips){
49525                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49526                 }
49527                 if(config.collapsible){
49528                     this.split.el.on("dblclick", this.collapse,  this);
49529                 }
49530             }
49531             if(typeof config.minSize != "undefined"){
49532                 this.split.minSize = config.minSize;
49533             }
49534             if(typeof config.maxSize != "undefined"){
49535                 this.split.maxSize = config.maxSize;
49536             }
49537             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49538                 this.hideSplitter();
49539             }
49540         }
49541     },
49542
49543     getHMaxSize : function(){
49544          var cmax = this.config.maxSize || 10000;
49545          var center = this.mgr.getRegion("center");
49546          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49547     },
49548
49549     getVMaxSize : function(){
49550          var cmax = this.config.maxSize || 10000;
49551          var center = this.mgr.getRegion("center");
49552          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49553     },
49554
49555     onSplitMove : function(split, newSize){
49556         this.fireEvent("resized", this, newSize);
49557     },
49558     
49559     /** 
49560      * Returns the {@link Roo.SplitBar} for this region.
49561      * @return {Roo.SplitBar}
49562      */
49563     getSplitBar : function(){
49564         return this.split;
49565     },
49566     
49567     hide : function(){
49568         this.hideSplitter();
49569         Roo.SplitLayoutRegion.superclass.hide.call(this);
49570     },
49571
49572     hideSplitter : function(){
49573         if(this.split){
49574             this.split.el.setLocation(-2000,-2000);
49575             this.split.el.hide();
49576         }
49577     },
49578
49579     show : function(){
49580         if(this.split){
49581             this.split.el.show();
49582         }
49583         Roo.SplitLayoutRegion.superclass.show.call(this);
49584     },
49585     
49586     beforeSlide: function(){
49587         if(Roo.isGecko){// firefox overflow auto bug workaround
49588             this.bodyEl.clip();
49589             if(this.tabs) this.tabs.bodyEl.clip();
49590             if(this.activePanel){
49591                 this.activePanel.getEl().clip();
49592                 
49593                 if(this.activePanel.beforeSlide){
49594                     this.activePanel.beforeSlide();
49595                 }
49596             }
49597         }
49598     },
49599     
49600     afterSlide : function(){
49601         if(Roo.isGecko){// firefox overflow auto bug workaround
49602             this.bodyEl.unclip();
49603             if(this.tabs) this.tabs.bodyEl.unclip();
49604             if(this.activePanel){
49605                 this.activePanel.getEl().unclip();
49606                 if(this.activePanel.afterSlide){
49607                     this.activePanel.afterSlide();
49608                 }
49609             }
49610         }
49611     },
49612
49613     initAutoHide : function(){
49614         if(this.autoHide !== false){
49615             if(!this.autoHideHd){
49616                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49617                 this.autoHideHd = {
49618                     "mouseout": function(e){
49619                         if(!e.within(this.el, true)){
49620                             st.delay(500);
49621                         }
49622                     },
49623                     "mouseover" : function(e){
49624                         st.cancel();
49625                     },
49626                     scope : this
49627                 };
49628             }
49629             this.el.on(this.autoHideHd);
49630         }
49631     },
49632
49633     clearAutoHide : function(){
49634         if(this.autoHide !== false){
49635             this.el.un("mouseout", this.autoHideHd.mouseout);
49636             this.el.un("mouseover", this.autoHideHd.mouseover);
49637         }
49638     },
49639
49640     clearMonitor : function(){
49641         Roo.get(document).un("click", this.slideInIf, this);
49642     },
49643
49644     // these names are backwards but not changed for compat
49645     slideOut : function(){
49646         if(this.isSlid || this.el.hasActiveFx()){
49647             return;
49648         }
49649         this.isSlid = true;
49650         if(this.collapseBtn){
49651             this.collapseBtn.hide();
49652         }
49653         this.closeBtnState = this.closeBtn.getStyle('display');
49654         this.closeBtn.hide();
49655         if(this.stickBtn){
49656             this.stickBtn.show();
49657         }
49658         this.el.show();
49659         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49660         this.beforeSlide();
49661         this.el.setStyle("z-index", 10001);
49662         this.el.slideIn(this.getSlideAnchor(), {
49663             callback: function(){
49664                 this.afterSlide();
49665                 this.initAutoHide();
49666                 Roo.get(document).on("click", this.slideInIf, this);
49667                 this.fireEvent("slideshow", this);
49668             },
49669             scope: this,
49670             block: true
49671         });
49672     },
49673
49674     afterSlideIn : function(){
49675         this.clearAutoHide();
49676         this.isSlid = false;
49677         this.clearMonitor();
49678         this.el.setStyle("z-index", "");
49679         if(this.collapseBtn){
49680             this.collapseBtn.show();
49681         }
49682         this.closeBtn.setStyle('display', this.closeBtnState);
49683         if(this.stickBtn){
49684             this.stickBtn.hide();
49685         }
49686         this.fireEvent("slidehide", this);
49687     },
49688
49689     slideIn : function(cb){
49690         if(!this.isSlid || this.el.hasActiveFx()){
49691             Roo.callback(cb);
49692             return;
49693         }
49694         this.isSlid = false;
49695         this.beforeSlide();
49696         this.el.slideOut(this.getSlideAnchor(), {
49697             callback: function(){
49698                 this.el.setLeftTop(-10000, -10000);
49699                 this.afterSlide();
49700                 this.afterSlideIn();
49701                 Roo.callback(cb);
49702             },
49703             scope: this,
49704             block: true
49705         });
49706     },
49707     
49708     slideInIf : function(e){
49709         if(!e.within(this.el)){
49710             this.slideIn();
49711         }
49712     },
49713
49714     animateCollapse : function(){
49715         this.beforeSlide();
49716         this.el.setStyle("z-index", 20000);
49717         var anchor = this.getSlideAnchor();
49718         this.el.slideOut(anchor, {
49719             callback : function(){
49720                 this.el.setStyle("z-index", "");
49721                 this.collapsedEl.slideIn(anchor, {duration:.3});
49722                 this.afterSlide();
49723                 this.el.setLocation(-10000,-10000);
49724                 this.el.hide();
49725                 this.fireEvent("collapsed", this);
49726             },
49727             scope: this,
49728             block: true
49729         });
49730     },
49731
49732     animateExpand : function(){
49733         this.beforeSlide();
49734         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49735         this.el.setStyle("z-index", 20000);
49736         this.collapsedEl.hide({
49737             duration:.1
49738         });
49739         this.el.slideIn(this.getSlideAnchor(), {
49740             callback : function(){
49741                 this.el.setStyle("z-index", "");
49742                 this.afterSlide();
49743                 if(this.split){
49744                     this.split.el.show();
49745                 }
49746                 this.fireEvent("invalidated", this);
49747                 this.fireEvent("expanded", this);
49748             },
49749             scope: this,
49750             block: true
49751         });
49752     },
49753
49754     anchors : {
49755         "west" : "left",
49756         "east" : "right",
49757         "north" : "top",
49758         "south" : "bottom"
49759     },
49760
49761     sanchors : {
49762         "west" : "l",
49763         "east" : "r",
49764         "north" : "t",
49765         "south" : "b"
49766     },
49767
49768     canchors : {
49769         "west" : "tl-tr",
49770         "east" : "tr-tl",
49771         "north" : "tl-bl",
49772         "south" : "bl-tl"
49773     },
49774
49775     getAnchor : function(){
49776         return this.anchors[this.position];
49777     },
49778
49779     getCollapseAnchor : function(){
49780         return this.canchors[this.position];
49781     },
49782
49783     getSlideAnchor : function(){
49784         return this.sanchors[this.position];
49785     },
49786
49787     getAlignAdj : function(){
49788         var cm = this.cmargins;
49789         switch(this.position){
49790             case "west":
49791                 return [0, 0];
49792             break;
49793             case "east":
49794                 return [0, 0];
49795             break;
49796             case "north":
49797                 return [0, 0];
49798             break;
49799             case "south":
49800                 return [0, 0];
49801             break;
49802         }
49803     },
49804
49805     getExpandAdj : function(){
49806         var c = this.collapsedEl, cm = this.cmargins;
49807         switch(this.position){
49808             case "west":
49809                 return [-(cm.right+c.getWidth()+cm.left), 0];
49810             break;
49811             case "east":
49812                 return [cm.right+c.getWidth()+cm.left, 0];
49813             break;
49814             case "north":
49815                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49816             break;
49817             case "south":
49818                 return [0, cm.top+cm.bottom+c.getHeight()];
49819             break;
49820         }
49821     }
49822 });/*
49823  * Based on:
49824  * Ext JS Library 1.1.1
49825  * Copyright(c) 2006-2007, Ext JS, LLC.
49826  *
49827  * Originally Released Under LGPL - original licence link has changed is not relivant.
49828  *
49829  * Fork - LGPL
49830  * <script type="text/javascript">
49831  */
49832 /*
49833  * These classes are private internal classes
49834  */
49835 Roo.CenterLayoutRegion = function(mgr, config){
49836     Roo.LayoutRegion.call(this, mgr, config, "center");
49837     this.visible = true;
49838     this.minWidth = config.minWidth || 20;
49839     this.minHeight = config.minHeight || 20;
49840 };
49841
49842 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49843     hide : function(){
49844         // center panel can't be hidden
49845     },
49846     
49847     show : function(){
49848         // center panel can't be hidden
49849     },
49850     
49851     getMinWidth: function(){
49852         return this.minWidth;
49853     },
49854     
49855     getMinHeight: function(){
49856         return this.minHeight;
49857     }
49858 });
49859
49860
49861 Roo.NorthLayoutRegion = function(mgr, config){
49862     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49863     if(this.split){
49864         this.split.placement = Roo.SplitBar.TOP;
49865         this.split.orientation = Roo.SplitBar.VERTICAL;
49866         this.split.el.addClass("x-layout-split-v");
49867     }
49868     var size = config.initialSize || config.height;
49869     if(typeof size != "undefined"){
49870         this.el.setHeight(size);
49871     }
49872 };
49873 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49874     orientation: Roo.SplitBar.VERTICAL,
49875     getBox : function(){
49876         if(this.collapsed){
49877             return this.collapsedEl.getBox();
49878         }
49879         var box = this.el.getBox();
49880         if(this.split){
49881             box.height += this.split.el.getHeight();
49882         }
49883         return box;
49884     },
49885     
49886     updateBox : function(box){
49887         if(this.split && !this.collapsed){
49888             box.height -= this.split.el.getHeight();
49889             this.split.el.setLeft(box.x);
49890             this.split.el.setTop(box.y+box.height);
49891             this.split.el.setWidth(box.width);
49892         }
49893         if(this.collapsed){
49894             this.updateBody(box.width, null);
49895         }
49896         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49897     }
49898 });
49899
49900 Roo.SouthLayoutRegion = function(mgr, config){
49901     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49902     if(this.split){
49903         this.split.placement = Roo.SplitBar.BOTTOM;
49904         this.split.orientation = Roo.SplitBar.VERTICAL;
49905         this.split.el.addClass("x-layout-split-v");
49906     }
49907     var size = config.initialSize || config.height;
49908     if(typeof size != "undefined"){
49909         this.el.setHeight(size);
49910     }
49911 };
49912 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49913     orientation: Roo.SplitBar.VERTICAL,
49914     getBox : function(){
49915         if(this.collapsed){
49916             return this.collapsedEl.getBox();
49917         }
49918         var box = this.el.getBox();
49919         if(this.split){
49920             var sh = this.split.el.getHeight();
49921             box.height += sh;
49922             box.y -= sh;
49923         }
49924         return box;
49925     },
49926     
49927     updateBox : function(box){
49928         if(this.split && !this.collapsed){
49929             var sh = this.split.el.getHeight();
49930             box.height -= sh;
49931             box.y += sh;
49932             this.split.el.setLeft(box.x);
49933             this.split.el.setTop(box.y-sh);
49934             this.split.el.setWidth(box.width);
49935         }
49936         if(this.collapsed){
49937             this.updateBody(box.width, null);
49938         }
49939         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49940     }
49941 });
49942
49943 Roo.EastLayoutRegion = function(mgr, config){
49944     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49945     if(this.split){
49946         this.split.placement = Roo.SplitBar.RIGHT;
49947         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49948         this.split.el.addClass("x-layout-split-h");
49949     }
49950     var size = config.initialSize || config.width;
49951     if(typeof size != "undefined"){
49952         this.el.setWidth(size);
49953     }
49954 };
49955 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49956     orientation: Roo.SplitBar.HORIZONTAL,
49957     getBox : function(){
49958         if(this.collapsed){
49959             return this.collapsedEl.getBox();
49960         }
49961         var box = this.el.getBox();
49962         if(this.split){
49963             var sw = this.split.el.getWidth();
49964             box.width += sw;
49965             box.x -= sw;
49966         }
49967         return box;
49968     },
49969
49970     updateBox : function(box){
49971         if(this.split && !this.collapsed){
49972             var sw = this.split.el.getWidth();
49973             box.width -= sw;
49974             this.split.el.setLeft(box.x);
49975             this.split.el.setTop(box.y);
49976             this.split.el.setHeight(box.height);
49977             box.x += sw;
49978         }
49979         if(this.collapsed){
49980             this.updateBody(null, box.height);
49981         }
49982         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49983     }
49984 });
49985
49986 Roo.WestLayoutRegion = function(mgr, config){
49987     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49988     if(this.split){
49989         this.split.placement = Roo.SplitBar.LEFT;
49990         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49991         this.split.el.addClass("x-layout-split-h");
49992     }
49993     var size = config.initialSize || config.width;
49994     if(typeof size != "undefined"){
49995         this.el.setWidth(size);
49996     }
49997 };
49998 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49999     orientation: Roo.SplitBar.HORIZONTAL,
50000     getBox : function(){
50001         if(this.collapsed){
50002             return this.collapsedEl.getBox();
50003         }
50004         var box = this.el.getBox();
50005         if(this.split){
50006             box.width += this.split.el.getWidth();
50007         }
50008         return box;
50009     },
50010     
50011     updateBox : function(box){
50012         if(this.split && !this.collapsed){
50013             var sw = this.split.el.getWidth();
50014             box.width -= sw;
50015             this.split.el.setLeft(box.x+box.width);
50016             this.split.el.setTop(box.y);
50017             this.split.el.setHeight(box.height);
50018         }
50019         if(this.collapsed){
50020             this.updateBody(null, box.height);
50021         }
50022         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50023     }
50024 });
50025 /*
50026  * Based on:
50027  * Ext JS Library 1.1.1
50028  * Copyright(c) 2006-2007, Ext JS, LLC.
50029  *
50030  * Originally Released Under LGPL - original licence link has changed is not relivant.
50031  *
50032  * Fork - LGPL
50033  * <script type="text/javascript">
50034  */
50035  
50036  
50037 /*
50038  * Private internal class for reading and applying state
50039  */
50040 Roo.LayoutStateManager = function(layout){
50041      // default empty state
50042      this.state = {
50043         north: {},
50044         south: {},
50045         east: {},
50046         west: {}       
50047     };
50048 };
50049
50050 Roo.LayoutStateManager.prototype = {
50051     init : function(layout, provider){
50052         this.provider = provider;
50053         var state = provider.get(layout.id+"-layout-state");
50054         if(state){
50055             var wasUpdating = layout.isUpdating();
50056             if(!wasUpdating){
50057                 layout.beginUpdate();
50058             }
50059             for(var key in state){
50060                 if(typeof state[key] != "function"){
50061                     var rstate = state[key];
50062                     var r = layout.getRegion(key);
50063                     if(r && rstate){
50064                         if(rstate.size){
50065                             r.resizeTo(rstate.size);
50066                         }
50067                         if(rstate.collapsed == true){
50068                             r.collapse(true);
50069                         }else{
50070                             r.expand(null, true);
50071                         }
50072                     }
50073                 }
50074             }
50075             if(!wasUpdating){
50076                 layout.endUpdate();
50077             }
50078             this.state = state; 
50079         }
50080         this.layout = layout;
50081         layout.on("regionresized", this.onRegionResized, this);
50082         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50083         layout.on("regionexpanded", this.onRegionExpanded, this);
50084     },
50085     
50086     storeState : function(){
50087         this.provider.set(this.layout.id+"-layout-state", this.state);
50088     },
50089     
50090     onRegionResized : function(region, newSize){
50091         this.state[region.getPosition()].size = newSize;
50092         this.storeState();
50093     },
50094     
50095     onRegionCollapsed : function(region){
50096         this.state[region.getPosition()].collapsed = true;
50097         this.storeState();
50098     },
50099     
50100     onRegionExpanded : function(region){
50101         this.state[region.getPosition()].collapsed = false;
50102         this.storeState();
50103     }
50104 };/*
50105  * Based on:
50106  * Ext JS Library 1.1.1
50107  * Copyright(c) 2006-2007, Ext JS, LLC.
50108  *
50109  * Originally Released Under LGPL - original licence link has changed is not relivant.
50110  *
50111  * Fork - LGPL
50112  * <script type="text/javascript">
50113  */
50114 /**
50115  * @class Roo.ContentPanel
50116  * @extends Roo.util.Observable
50117  * A basic ContentPanel element.
50118  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50119  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50120  * @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
50121  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50122  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50123  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50124  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50125  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50126  * @cfg {String} title          The title for this panel
50127  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50128  * @cfg {String} url            Calls {@link #setUrl} with this value
50129  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50130  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50131  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50132  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50133
50134  * @constructor
50135  * Create a new ContentPanel.
50136  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50137  * @param {String/Object} config A string to set only the title or a config object
50138  * @param {String} content (optional) Set the HTML content for this panel
50139  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50140  */
50141 Roo.ContentPanel = function(el, config, content){
50142     
50143      
50144     /*
50145     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50146         config = el;
50147         el = Roo.id();
50148     }
50149     if (config && config.parentLayout) { 
50150         el = config.parentLayout.el.createChild(); 
50151     }
50152     */
50153     if(el.autoCreate){ // xtype is available if this is called from factory
50154         config = el;
50155         el = Roo.id();
50156     }
50157     this.el = Roo.get(el);
50158     if(!this.el && config && config.autoCreate){
50159         if(typeof config.autoCreate == "object"){
50160             if(!config.autoCreate.id){
50161                 config.autoCreate.id = config.id||el;
50162             }
50163             this.el = Roo.DomHelper.append(document.body,
50164                         config.autoCreate, true);
50165         }else{
50166             this.el = Roo.DomHelper.append(document.body,
50167                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50168         }
50169     }
50170     this.closable = false;
50171     this.loaded = false;
50172     this.active = false;
50173     if(typeof config == "string"){
50174         this.title = config;
50175     }else{
50176         Roo.apply(this, config);
50177     }
50178     
50179     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50180         this.wrapEl = this.el.wrap();
50181         this.toolbar.container = this.el.insertSibling(false, 'before');
50182         this.toolbar = new Roo.Toolbar(this.toolbar);
50183     }
50184     
50185     // xtype created footer. - not sure if will work as we normally have to render first..
50186     if (this.footer && !this.footer.el && this.footer.xtype) {
50187         if (!this.wrapEl) {
50188             this.wrapEl = this.el.wrap();
50189         }
50190     
50191         this.footer.container = this.wrapEl.createChild();
50192          
50193         this.footer = Roo.factory(this.footer, Roo);
50194         
50195     }
50196     
50197     if(this.resizeEl){
50198         this.resizeEl = Roo.get(this.resizeEl, true);
50199     }else{
50200         this.resizeEl = this.el;
50201     }
50202     // handle view.xtype
50203     
50204  
50205     
50206     
50207     this.addEvents({
50208         /**
50209          * @event activate
50210          * Fires when this panel is activated. 
50211          * @param {Roo.ContentPanel} this
50212          */
50213         "activate" : true,
50214         /**
50215          * @event deactivate
50216          * Fires when this panel is activated. 
50217          * @param {Roo.ContentPanel} this
50218          */
50219         "deactivate" : true,
50220
50221         /**
50222          * @event resize
50223          * Fires when this panel is resized if fitToFrame is true.
50224          * @param {Roo.ContentPanel} this
50225          * @param {Number} width The width after any component adjustments
50226          * @param {Number} height The height after any component adjustments
50227          */
50228         "resize" : true,
50229         
50230          /**
50231          * @event render
50232          * Fires when this tab is created
50233          * @param {Roo.ContentPanel} this
50234          */
50235         "render" : true
50236         
50237         
50238         
50239     });
50240     
50241
50242     
50243     
50244     if(this.autoScroll){
50245         this.resizeEl.setStyle("overflow", "auto");
50246     } else {
50247         // fix randome scrolling
50248         this.el.on('scroll', function() {
50249             Roo.log('fix random scolling');
50250             this.scrollTo('top',0); 
50251         });
50252     }
50253     content = content || this.content;
50254     if(content){
50255         this.setContent(content);
50256     }
50257     if(config && config.url){
50258         this.setUrl(this.url, this.params, this.loadOnce);
50259     }
50260     
50261     
50262     
50263     Roo.ContentPanel.superclass.constructor.call(this);
50264     
50265     if (this.view && typeof(this.view.xtype) != 'undefined') {
50266         this.view.el = this.el.appendChild(document.createElement("div"));
50267         this.view = Roo.factory(this.view); 
50268         this.view.render  &&  this.view.render(false, '');  
50269     }
50270     
50271     
50272     this.fireEvent('render', this);
50273 };
50274
50275 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50276     tabTip:'',
50277     setRegion : function(region){
50278         this.region = region;
50279         if(region){
50280            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50281         }else{
50282            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50283         } 
50284     },
50285     
50286     /**
50287      * Returns the toolbar for this Panel if one was configured. 
50288      * @return {Roo.Toolbar} 
50289      */
50290     getToolbar : function(){
50291         return this.toolbar;
50292     },
50293     
50294     setActiveState : function(active){
50295         this.active = active;
50296         if(!active){
50297             this.fireEvent("deactivate", this);
50298         }else{
50299             this.fireEvent("activate", this);
50300         }
50301     },
50302     /**
50303      * Updates this panel's element
50304      * @param {String} content The new content
50305      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50306     */
50307     setContent : function(content, loadScripts){
50308         this.el.update(content, loadScripts);
50309     },
50310
50311     ignoreResize : function(w, h){
50312         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50313             return true;
50314         }else{
50315             this.lastSize = {width: w, height: h};
50316             return false;
50317         }
50318     },
50319     /**
50320      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50321      * @return {Roo.UpdateManager} The UpdateManager
50322      */
50323     getUpdateManager : function(){
50324         return this.el.getUpdateManager();
50325     },
50326      /**
50327      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50328      * @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:
50329 <pre><code>
50330 panel.load({
50331     url: "your-url.php",
50332     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50333     callback: yourFunction,
50334     scope: yourObject, //(optional scope)
50335     discardUrl: false,
50336     nocache: false,
50337     text: "Loading...",
50338     timeout: 30,
50339     scripts: false
50340 });
50341 </code></pre>
50342      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50343      * 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.
50344      * @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}
50345      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50346      * @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.
50347      * @return {Roo.ContentPanel} this
50348      */
50349     load : function(){
50350         var um = this.el.getUpdateManager();
50351         um.update.apply(um, arguments);
50352         return this;
50353     },
50354
50355
50356     /**
50357      * 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.
50358      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50359      * @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)
50360      * @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)
50361      * @return {Roo.UpdateManager} The UpdateManager
50362      */
50363     setUrl : function(url, params, loadOnce){
50364         if(this.refreshDelegate){
50365             this.removeListener("activate", this.refreshDelegate);
50366         }
50367         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50368         this.on("activate", this.refreshDelegate);
50369         return this.el.getUpdateManager();
50370     },
50371     
50372     _handleRefresh : function(url, params, loadOnce){
50373         if(!loadOnce || !this.loaded){
50374             var updater = this.el.getUpdateManager();
50375             updater.update(url, params, this._setLoaded.createDelegate(this));
50376         }
50377     },
50378     
50379     _setLoaded : function(){
50380         this.loaded = true;
50381     }, 
50382     
50383     /**
50384      * Returns this panel's id
50385      * @return {String} 
50386      */
50387     getId : function(){
50388         return this.el.id;
50389     },
50390     
50391     /** 
50392      * Returns this panel's element - used by regiosn to add.
50393      * @return {Roo.Element} 
50394      */
50395     getEl : function(){
50396         return this.wrapEl || this.el;
50397     },
50398     
50399     adjustForComponents : function(width, height)
50400     {
50401         //Roo.log('adjustForComponents ');
50402         if(this.resizeEl != this.el){
50403             width -= this.el.getFrameWidth('lr');
50404             height -= this.el.getFrameWidth('tb');
50405         }
50406         if(this.toolbar){
50407             var te = this.toolbar.getEl();
50408             height -= te.getHeight();
50409             te.setWidth(width);
50410         }
50411         if(this.footer){
50412             var te = this.footer.getEl();
50413             Roo.log("footer:" + te.getHeight());
50414             
50415             height -= te.getHeight();
50416             te.setWidth(width);
50417         }
50418         
50419         
50420         if(this.adjustments){
50421             width += this.adjustments[0];
50422             height += this.adjustments[1];
50423         }
50424         return {"width": width, "height": height};
50425     },
50426     
50427     setSize : function(width, height){
50428         if(this.fitToFrame && !this.ignoreResize(width, height)){
50429             if(this.fitContainer && this.resizeEl != this.el){
50430                 this.el.setSize(width, height);
50431             }
50432             var size = this.adjustForComponents(width, height);
50433             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50434             this.fireEvent('resize', this, size.width, size.height);
50435         }
50436     },
50437     
50438     /**
50439      * Returns this panel's title
50440      * @return {String} 
50441      */
50442     getTitle : function(){
50443         return this.title;
50444     },
50445     
50446     /**
50447      * Set this panel's title
50448      * @param {String} title
50449      */
50450     setTitle : function(title){
50451         this.title = title;
50452         if(this.region){
50453             this.region.updatePanelTitle(this, title);
50454         }
50455     },
50456     
50457     /**
50458      * Returns true is this panel was configured to be closable
50459      * @return {Boolean} 
50460      */
50461     isClosable : function(){
50462         return this.closable;
50463     },
50464     
50465     beforeSlide : function(){
50466         this.el.clip();
50467         this.resizeEl.clip();
50468     },
50469     
50470     afterSlide : function(){
50471         this.el.unclip();
50472         this.resizeEl.unclip();
50473     },
50474     
50475     /**
50476      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50477      *   Will fail silently if the {@link #setUrl} method has not been called.
50478      *   This does not activate the panel, just updates its content.
50479      */
50480     refresh : function(){
50481         if(this.refreshDelegate){
50482            this.loaded = false;
50483            this.refreshDelegate();
50484         }
50485     },
50486     
50487     /**
50488      * Destroys this panel
50489      */
50490     destroy : function(){
50491         this.el.removeAllListeners();
50492         var tempEl = document.createElement("span");
50493         tempEl.appendChild(this.el.dom);
50494         tempEl.innerHTML = "";
50495         this.el.remove();
50496         this.el = null;
50497     },
50498     
50499     /**
50500      * form - if the content panel contains a form - this is a reference to it.
50501      * @type {Roo.form.Form}
50502      */
50503     form : false,
50504     /**
50505      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50506      *    This contains a reference to it.
50507      * @type {Roo.View}
50508      */
50509     view : false,
50510     
50511       /**
50512      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50513      * <pre><code>
50514
50515 layout.addxtype({
50516        xtype : 'Form',
50517        items: [ .... ]
50518    }
50519 );
50520
50521 </code></pre>
50522      * @param {Object} cfg Xtype definition of item to add.
50523      */
50524     
50525     addxtype : function(cfg) {
50526         // add form..
50527         if (cfg.xtype.match(/^Form$/)) {
50528             
50529             var el;
50530             //if (this.footer) {
50531             //    el = this.footer.container.insertSibling(false, 'before');
50532             //} else {
50533                 el = this.el.createChild();
50534             //}
50535
50536             this.form = new  Roo.form.Form(cfg);
50537             
50538             
50539             if ( this.form.allItems.length) this.form.render(el.dom);
50540             return this.form;
50541         }
50542         // should only have one of theses..
50543         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50544             // views.. should not be just added - used named prop 'view''
50545             
50546             cfg.el = this.el.appendChild(document.createElement("div"));
50547             // factory?
50548             
50549             var ret = new Roo.factory(cfg);
50550              
50551              ret.render && ret.render(false, ''); // render blank..
50552             this.view = ret;
50553             return ret;
50554         }
50555         return false;
50556     }
50557 });
50558
50559 /**
50560  * @class Roo.GridPanel
50561  * @extends Roo.ContentPanel
50562  * @constructor
50563  * Create a new GridPanel.
50564  * @param {Roo.grid.Grid} grid The grid for this panel
50565  * @param {String/Object} config A string to set only the panel's title, or a config object
50566  */
50567 Roo.GridPanel = function(grid, config){
50568     
50569   
50570     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50571         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50572         
50573     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50574     
50575     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50576     
50577     if(this.toolbar){
50578         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50579     }
50580     // xtype created footer. - not sure if will work as we normally have to render first..
50581     if (this.footer && !this.footer.el && this.footer.xtype) {
50582         
50583         this.footer.container = this.grid.getView().getFooterPanel(true);
50584         this.footer.dataSource = this.grid.dataSource;
50585         this.footer = Roo.factory(this.footer, Roo);
50586         
50587     }
50588     
50589     grid.monitorWindowResize = false; // turn off autosizing
50590     grid.autoHeight = false;
50591     grid.autoWidth = false;
50592     this.grid = grid;
50593     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50594 };
50595
50596 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50597     getId : function(){
50598         return this.grid.id;
50599     },
50600     
50601     /**
50602      * Returns the grid for this panel
50603      * @return {Roo.grid.Grid} 
50604      */
50605     getGrid : function(){
50606         return this.grid;    
50607     },
50608     
50609     setSize : function(width, height){
50610         if(!this.ignoreResize(width, height)){
50611             var grid = this.grid;
50612             var size = this.adjustForComponents(width, height);
50613             grid.getGridEl().setSize(size.width, size.height);
50614             grid.autoSize();
50615         }
50616     },
50617     
50618     beforeSlide : function(){
50619         this.grid.getView().scroller.clip();
50620     },
50621     
50622     afterSlide : function(){
50623         this.grid.getView().scroller.unclip();
50624     },
50625     
50626     destroy : function(){
50627         this.grid.destroy();
50628         delete this.grid;
50629         Roo.GridPanel.superclass.destroy.call(this); 
50630     }
50631 });
50632
50633
50634 /**
50635  * @class Roo.NestedLayoutPanel
50636  * @extends Roo.ContentPanel
50637  * @constructor
50638  * Create a new NestedLayoutPanel.
50639  * 
50640  * 
50641  * @param {Roo.BorderLayout} layout The layout for this panel
50642  * @param {String/Object} config A string to set only the title or a config object
50643  */
50644 Roo.NestedLayoutPanel = function(layout, config)
50645 {
50646     // construct with only one argument..
50647     /* FIXME - implement nicer consturctors
50648     if (layout.layout) {
50649         config = layout;
50650         layout = config.layout;
50651         delete config.layout;
50652     }
50653     if (layout.xtype && !layout.getEl) {
50654         // then layout needs constructing..
50655         layout = Roo.factory(layout, Roo);
50656     }
50657     */
50658     
50659     
50660     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50661     
50662     layout.monitorWindowResize = false; // turn off autosizing
50663     this.layout = layout;
50664     this.layout.getEl().addClass("x-layout-nested-layout");
50665     
50666     
50667     
50668     
50669 };
50670
50671 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50672
50673     setSize : function(width, height){
50674         if(!this.ignoreResize(width, height)){
50675             var size = this.adjustForComponents(width, height);
50676             var el = this.layout.getEl();
50677             el.setSize(size.width, size.height);
50678             var touch = el.dom.offsetWidth;
50679             this.layout.layout();
50680             // ie requires a double layout on the first pass
50681             if(Roo.isIE && !this.initialized){
50682                 this.initialized = true;
50683                 this.layout.layout();
50684             }
50685         }
50686     },
50687     
50688     // activate all subpanels if not currently active..
50689     
50690     setActiveState : function(active){
50691         this.active = active;
50692         if(!active){
50693             this.fireEvent("deactivate", this);
50694             return;
50695         }
50696         
50697         this.fireEvent("activate", this);
50698         // not sure if this should happen before or after..
50699         if (!this.layout) {
50700             return; // should not happen..
50701         }
50702         var reg = false;
50703         for (var r in this.layout.regions) {
50704             reg = this.layout.getRegion(r);
50705             if (reg.getActivePanel()) {
50706                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50707                 reg.setActivePanel(reg.getActivePanel());
50708                 continue;
50709             }
50710             if (!reg.panels.length) {
50711                 continue;
50712             }
50713             reg.showPanel(reg.getPanel(0));
50714         }
50715         
50716         
50717         
50718         
50719     },
50720     
50721     /**
50722      * Returns the nested BorderLayout for this panel
50723      * @return {Roo.BorderLayout} 
50724      */
50725     getLayout : function(){
50726         return this.layout;
50727     },
50728     
50729      /**
50730      * Adds a xtype elements to the layout of the nested panel
50731      * <pre><code>
50732
50733 panel.addxtype({
50734        xtype : 'ContentPanel',
50735        region: 'west',
50736        items: [ .... ]
50737    }
50738 );
50739
50740 panel.addxtype({
50741         xtype : 'NestedLayoutPanel',
50742         region: 'west',
50743         layout: {
50744            center: { },
50745            west: { }   
50746         },
50747         items : [ ... list of content panels or nested layout panels.. ]
50748    }
50749 );
50750 </code></pre>
50751      * @param {Object} cfg Xtype definition of item to add.
50752      */
50753     addxtype : function(cfg) {
50754         return this.layout.addxtype(cfg);
50755     
50756     }
50757 });
50758
50759 Roo.ScrollPanel = function(el, config, content){
50760     config = config || {};
50761     config.fitToFrame = true;
50762     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50763     
50764     this.el.dom.style.overflow = "hidden";
50765     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50766     this.el.removeClass("x-layout-inactive-content");
50767     this.el.on("mousewheel", this.onWheel, this);
50768
50769     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50770     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50771     up.unselectable(); down.unselectable();
50772     up.on("click", this.scrollUp, this);
50773     down.on("click", this.scrollDown, this);
50774     up.addClassOnOver("x-scroller-btn-over");
50775     down.addClassOnOver("x-scroller-btn-over");
50776     up.addClassOnClick("x-scroller-btn-click");
50777     down.addClassOnClick("x-scroller-btn-click");
50778     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50779
50780     this.resizeEl = this.el;
50781     this.el = wrap; this.up = up; this.down = down;
50782 };
50783
50784 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50785     increment : 100,
50786     wheelIncrement : 5,
50787     scrollUp : function(){
50788         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50789     },
50790
50791     scrollDown : function(){
50792         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50793     },
50794
50795     afterScroll : function(){
50796         var el = this.resizeEl;
50797         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50798         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50799         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50800     },
50801
50802     setSize : function(){
50803         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50804         this.afterScroll();
50805     },
50806
50807     onWheel : function(e){
50808         var d = e.getWheelDelta();
50809         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50810         this.afterScroll();
50811         e.stopEvent();
50812     },
50813
50814     setContent : function(content, loadScripts){
50815         this.resizeEl.update(content, loadScripts);
50816     }
50817
50818 });
50819
50820
50821
50822
50823
50824
50825
50826
50827
50828 /**
50829  * @class Roo.TreePanel
50830  * @extends Roo.ContentPanel
50831  * @constructor
50832  * Create a new TreePanel. - defaults to fit/scoll contents.
50833  * @param {String/Object} config A string to set only the panel's title, or a config object
50834  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50835  */
50836 Roo.TreePanel = function(config){
50837     var el = config.el;
50838     var tree = config.tree;
50839     delete config.tree; 
50840     delete config.el; // hopefull!
50841     
50842     // wrapper for IE7 strict & safari scroll issue
50843     
50844     var treeEl = el.createChild();
50845     config.resizeEl = treeEl;
50846     
50847     
50848     
50849     Roo.TreePanel.superclass.constructor.call(this, el, config);
50850  
50851  
50852     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50853     //console.log(tree);
50854     this.on('activate', function()
50855     {
50856         if (this.tree.rendered) {
50857             return;
50858         }
50859         //console.log('render tree');
50860         this.tree.render();
50861     });
50862     // this should not be needed.. - it's actually the 'el' that resizes?
50863     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50864     
50865     //this.on('resize',  function (cp, w, h) {
50866     //        this.tree.innerCt.setWidth(w);
50867     //        this.tree.innerCt.setHeight(h);
50868     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50869     //});
50870
50871         
50872     
50873 };
50874
50875 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50876     fitToFrame : true,
50877     autoScroll : true
50878 });
50879
50880
50881
50882
50883
50884
50885
50886
50887
50888
50889
50890 /*
50891  * Based on:
50892  * Ext JS Library 1.1.1
50893  * Copyright(c) 2006-2007, Ext JS, LLC.
50894  *
50895  * Originally Released Under LGPL - original licence link has changed is not relivant.
50896  *
50897  * Fork - LGPL
50898  * <script type="text/javascript">
50899  */
50900  
50901
50902 /**
50903  * @class Roo.ReaderLayout
50904  * @extends Roo.BorderLayout
50905  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50906  * center region containing two nested regions (a top one for a list view and one for item preview below),
50907  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50908  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50909  * expedites the setup of the overall layout and regions for this common application style.
50910  * Example:
50911  <pre><code>
50912 var reader = new Roo.ReaderLayout();
50913 var CP = Roo.ContentPanel;  // shortcut for adding
50914
50915 reader.beginUpdate();
50916 reader.add("north", new CP("north", "North"));
50917 reader.add("west", new CP("west", {title: "West"}));
50918 reader.add("east", new CP("east", {title: "East"}));
50919
50920 reader.regions.listView.add(new CP("listView", "List"));
50921 reader.regions.preview.add(new CP("preview", "Preview"));
50922 reader.endUpdate();
50923 </code></pre>
50924 * @constructor
50925 * Create a new ReaderLayout
50926 * @param {Object} config Configuration options
50927 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50928 * document.body if omitted)
50929 */
50930 Roo.ReaderLayout = function(config, renderTo){
50931     var c = config || {size:{}};
50932     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50933         north: c.north !== false ? Roo.apply({
50934             split:false,
50935             initialSize: 32,
50936             titlebar: false
50937         }, c.north) : false,
50938         west: c.west !== false ? Roo.apply({
50939             split:true,
50940             initialSize: 200,
50941             minSize: 175,
50942             maxSize: 400,
50943             titlebar: true,
50944             collapsible: true,
50945             animate: true,
50946             margins:{left:5,right:0,bottom:5,top:5},
50947             cmargins:{left:5,right:5,bottom:5,top:5}
50948         }, c.west) : false,
50949         east: c.east !== false ? Roo.apply({
50950             split:true,
50951             initialSize: 200,
50952             minSize: 175,
50953             maxSize: 400,
50954             titlebar: true,
50955             collapsible: true,
50956             animate: true,
50957             margins:{left:0,right:5,bottom:5,top:5},
50958             cmargins:{left:5,right:5,bottom:5,top:5}
50959         }, c.east) : false,
50960         center: Roo.apply({
50961             tabPosition: 'top',
50962             autoScroll:false,
50963             closeOnTab: true,
50964             titlebar:false,
50965             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50966         }, c.center)
50967     });
50968
50969     this.el.addClass('x-reader');
50970
50971     this.beginUpdate();
50972
50973     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50974         south: c.preview !== false ? Roo.apply({
50975             split:true,
50976             initialSize: 200,
50977             minSize: 100,
50978             autoScroll:true,
50979             collapsible:true,
50980             titlebar: true,
50981             cmargins:{top:5,left:0, right:0, bottom:0}
50982         }, c.preview) : false,
50983         center: Roo.apply({
50984             autoScroll:false,
50985             titlebar:false,
50986             minHeight:200
50987         }, c.listView)
50988     });
50989     this.add('center', new Roo.NestedLayoutPanel(inner,
50990             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50991
50992     this.endUpdate();
50993
50994     this.regions.preview = inner.getRegion('south');
50995     this.regions.listView = inner.getRegion('center');
50996 };
50997
50998 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50999  * Based on:
51000  * Ext JS Library 1.1.1
51001  * Copyright(c) 2006-2007, Ext JS, LLC.
51002  *
51003  * Originally Released Under LGPL - original licence link has changed is not relivant.
51004  *
51005  * Fork - LGPL
51006  * <script type="text/javascript">
51007  */
51008  
51009 /**
51010  * @class Roo.grid.Grid
51011  * @extends Roo.util.Observable
51012  * This class represents the primary interface of a component based grid control.
51013  * <br><br>Usage:<pre><code>
51014  var grid = new Roo.grid.Grid("my-container-id", {
51015      ds: myDataStore,
51016      cm: myColModel,
51017      selModel: mySelectionModel,
51018      autoSizeColumns: true,
51019      monitorWindowResize: false,
51020      trackMouseOver: true
51021  });
51022  // set any options
51023  grid.render();
51024  * </code></pre>
51025  * <b>Common Problems:</b><br/>
51026  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51027  * element will correct this<br/>
51028  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51029  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51030  * are unpredictable.<br/>
51031  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51032  * grid to calculate dimensions/offsets.<br/>
51033   * @constructor
51034  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51035  * The container MUST have some type of size defined for the grid to fill. The container will be
51036  * automatically set to position relative if it isn't already.
51037  * @param {Object} config A config object that sets properties on this grid.
51038  */
51039 Roo.grid.Grid = function(container, config){
51040         // initialize the container
51041         this.container = Roo.get(container);
51042         this.container.update("");
51043         this.container.setStyle("overflow", "hidden");
51044     this.container.addClass('x-grid-container');
51045
51046     this.id = this.container.id;
51047
51048     Roo.apply(this, config);
51049     // check and correct shorthanded configs
51050     if(this.ds){
51051         this.dataSource = this.ds;
51052         delete this.ds;
51053     }
51054     if(this.cm){
51055         this.colModel = this.cm;
51056         delete this.cm;
51057     }
51058     if(this.sm){
51059         this.selModel = this.sm;
51060         delete this.sm;
51061     }
51062
51063     if (this.selModel) {
51064         this.selModel = Roo.factory(this.selModel, Roo.grid);
51065         this.sm = this.selModel;
51066         this.sm.xmodule = this.xmodule || false;
51067     }
51068     if (typeof(this.colModel.config) == 'undefined') {
51069         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51070         this.cm = this.colModel;
51071         this.cm.xmodule = this.xmodule || false;
51072     }
51073     if (this.dataSource) {
51074         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51075         this.ds = this.dataSource;
51076         this.ds.xmodule = this.xmodule || false;
51077          
51078     }
51079     
51080     
51081     
51082     if(this.width){
51083         this.container.setWidth(this.width);
51084     }
51085
51086     if(this.height){
51087         this.container.setHeight(this.height);
51088     }
51089     /** @private */
51090         this.addEvents({
51091         // raw events
51092         /**
51093          * @event click
51094          * The raw click event for the entire grid.
51095          * @param {Roo.EventObject} e
51096          */
51097         "click" : true,
51098         /**
51099          * @event dblclick
51100          * The raw dblclick event for the entire grid.
51101          * @param {Roo.EventObject} e
51102          */
51103         "dblclick" : true,
51104         /**
51105          * @event contextmenu
51106          * The raw contextmenu event for the entire grid.
51107          * @param {Roo.EventObject} e
51108          */
51109         "contextmenu" : true,
51110         /**
51111          * @event mousedown
51112          * The raw mousedown event for the entire grid.
51113          * @param {Roo.EventObject} e
51114          */
51115         "mousedown" : true,
51116         /**
51117          * @event mouseup
51118          * The raw mouseup event for the entire grid.
51119          * @param {Roo.EventObject} e
51120          */
51121         "mouseup" : true,
51122         /**
51123          * @event mouseover
51124          * The raw mouseover event for the entire grid.
51125          * @param {Roo.EventObject} e
51126          */
51127         "mouseover" : true,
51128         /**
51129          * @event mouseout
51130          * The raw mouseout event for the entire grid.
51131          * @param {Roo.EventObject} e
51132          */
51133         "mouseout" : true,
51134         /**
51135          * @event keypress
51136          * The raw keypress event for the entire grid.
51137          * @param {Roo.EventObject} e
51138          */
51139         "keypress" : true,
51140         /**
51141          * @event keydown
51142          * The raw keydown event for the entire grid.
51143          * @param {Roo.EventObject} e
51144          */
51145         "keydown" : true,
51146
51147         // custom events
51148
51149         /**
51150          * @event cellclick
51151          * Fires when a cell is clicked
51152          * @param {Grid} this
51153          * @param {Number} rowIndex
51154          * @param {Number} columnIndex
51155          * @param {Roo.EventObject} e
51156          */
51157         "cellclick" : true,
51158         /**
51159          * @event celldblclick
51160          * Fires when a cell is double clicked
51161          * @param {Grid} this
51162          * @param {Number} rowIndex
51163          * @param {Number} columnIndex
51164          * @param {Roo.EventObject} e
51165          */
51166         "celldblclick" : true,
51167         /**
51168          * @event rowclick
51169          * Fires when a row is clicked
51170          * @param {Grid} this
51171          * @param {Number} rowIndex
51172          * @param {Roo.EventObject} e
51173          */
51174         "rowclick" : true,
51175         /**
51176          * @event rowdblclick
51177          * Fires when a row is double clicked
51178          * @param {Grid} this
51179          * @param {Number} rowIndex
51180          * @param {Roo.EventObject} e
51181          */
51182         "rowdblclick" : true,
51183         /**
51184          * @event headerclick
51185          * Fires when a header is clicked
51186          * @param {Grid} this
51187          * @param {Number} columnIndex
51188          * @param {Roo.EventObject} e
51189          */
51190         "headerclick" : true,
51191         /**
51192          * @event headerdblclick
51193          * Fires when a header cell is double clicked
51194          * @param {Grid} this
51195          * @param {Number} columnIndex
51196          * @param {Roo.EventObject} e
51197          */
51198         "headerdblclick" : true,
51199         /**
51200          * @event rowcontextmenu
51201          * Fires when a row is right clicked
51202          * @param {Grid} this
51203          * @param {Number} rowIndex
51204          * @param {Roo.EventObject} e
51205          */
51206         "rowcontextmenu" : true,
51207         /**
51208          * @event cellcontextmenu
51209          * Fires when a cell is right clicked
51210          * @param {Grid} this
51211          * @param {Number} rowIndex
51212          * @param {Number} cellIndex
51213          * @param {Roo.EventObject} e
51214          */
51215          "cellcontextmenu" : true,
51216         /**
51217          * @event headercontextmenu
51218          * Fires when a header is right clicked
51219          * @param {Grid} this
51220          * @param {Number} columnIndex
51221          * @param {Roo.EventObject} e
51222          */
51223         "headercontextmenu" : true,
51224         /**
51225          * @event bodyscroll
51226          * Fires when the body element is scrolled
51227          * @param {Number} scrollLeft
51228          * @param {Number} scrollTop
51229          */
51230         "bodyscroll" : true,
51231         /**
51232          * @event columnresize
51233          * Fires when the user resizes a column
51234          * @param {Number} columnIndex
51235          * @param {Number} newSize
51236          */
51237         "columnresize" : true,
51238         /**
51239          * @event columnmove
51240          * Fires when the user moves a column
51241          * @param {Number} oldIndex
51242          * @param {Number} newIndex
51243          */
51244         "columnmove" : true,
51245         /**
51246          * @event startdrag
51247          * Fires when row(s) start being dragged
51248          * @param {Grid} this
51249          * @param {Roo.GridDD} dd The drag drop object
51250          * @param {event} e The raw browser event
51251          */
51252         "startdrag" : true,
51253         /**
51254          * @event enddrag
51255          * Fires when a drag operation is complete
51256          * @param {Grid} this
51257          * @param {Roo.GridDD} dd The drag drop object
51258          * @param {event} e The raw browser event
51259          */
51260         "enddrag" : true,
51261         /**
51262          * @event dragdrop
51263          * Fires when dragged row(s) are dropped on a valid DD target
51264          * @param {Grid} this
51265          * @param {Roo.GridDD} dd The drag drop object
51266          * @param {String} targetId The target drag drop object
51267          * @param {event} e The raw browser event
51268          */
51269         "dragdrop" : true,
51270         /**
51271          * @event dragover
51272          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51273          * @param {Grid} this
51274          * @param {Roo.GridDD} dd The drag drop object
51275          * @param {String} targetId The target drag drop object
51276          * @param {event} e The raw browser event
51277          */
51278         "dragover" : true,
51279         /**
51280          * @event dragenter
51281          *  Fires when the dragged row(s) first cross another DD target while being dragged
51282          * @param {Grid} this
51283          * @param {Roo.GridDD} dd The drag drop object
51284          * @param {String} targetId The target drag drop object
51285          * @param {event} e The raw browser event
51286          */
51287         "dragenter" : true,
51288         /**
51289          * @event dragout
51290          * Fires when the dragged row(s) leave another DD target while being dragged
51291          * @param {Grid} this
51292          * @param {Roo.GridDD} dd The drag drop object
51293          * @param {String} targetId The target drag drop object
51294          * @param {event} e The raw browser event
51295          */
51296         "dragout" : true,
51297         /**
51298          * @event rowclass
51299          * Fires when a row is rendered, so you can change add a style to it.
51300          * @param {GridView} gridview   The grid view
51301          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51302          */
51303         'rowclass' : true,
51304
51305         /**
51306          * @event render
51307          * Fires when the grid is rendered
51308          * @param {Grid} grid
51309          */
51310         'render' : true
51311     });
51312
51313     Roo.grid.Grid.superclass.constructor.call(this);
51314 };
51315 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51316     
51317     /**
51318      * @cfg {String} ddGroup - drag drop group.
51319      */
51320
51321     /**
51322      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51323      */
51324     minColumnWidth : 25,
51325
51326     /**
51327      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51328      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51329      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51330      */
51331     autoSizeColumns : false,
51332
51333     /**
51334      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51335      */
51336     autoSizeHeaders : true,
51337
51338     /**
51339      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51340      */
51341     monitorWindowResize : true,
51342
51343     /**
51344      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51345      * rows measured to get a columns size. Default is 0 (all rows).
51346      */
51347     maxRowsToMeasure : 0,
51348
51349     /**
51350      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51351      */
51352     trackMouseOver : true,
51353
51354     /**
51355     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51356     */
51357     
51358     /**
51359     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51360     */
51361     enableDragDrop : false,
51362     
51363     /**
51364     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51365     */
51366     enableColumnMove : true,
51367     
51368     /**
51369     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51370     */
51371     enableColumnHide : true,
51372     
51373     /**
51374     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51375     */
51376     enableRowHeightSync : false,
51377     
51378     /**
51379     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51380     */
51381     stripeRows : true,
51382     
51383     /**
51384     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51385     */
51386     autoHeight : false,
51387
51388     /**
51389      * @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.
51390      */
51391     autoExpandColumn : false,
51392
51393     /**
51394     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51395     * Default is 50.
51396     */
51397     autoExpandMin : 50,
51398
51399     /**
51400     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51401     */
51402     autoExpandMax : 1000,
51403
51404     /**
51405     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51406     */
51407     view : null,
51408
51409     /**
51410     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51411     */
51412     loadMask : false,
51413     /**
51414     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51415     */
51416     dropTarget: false,
51417     
51418    
51419     
51420     // private
51421     rendered : false,
51422
51423     /**
51424     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51425     * of a fixed width. Default is false.
51426     */
51427     /**
51428     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51429     */
51430     /**
51431      * Called once after all setup has been completed and the grid is ready to be rendered.
51432      * @return {Roo.grid.Grid} this
51433      */
51434     render : function()
51435     {
51436         var c = this.container;
51437         // try to detect autoHeight/width mode
51438         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51439             this.autoHeight = true;
51440         }
51441         var view = this.getView();
51442         view.init(this);
51443
51444         c.on("click", this.onClick, this);
51445         c.on("dblclick", this.onDblClick, this);
51446         c.on("contextmenu", this.onContextMenu, this);
51447         c.on("keydown", this.onKeyDown, this);
51448         if (Roo.isTouch) {
51449             c.on("touchstart", this.onTouchStart, this);
51450         }
51451
51452         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51453
51454         this.getSelectionModel().init(this);
51455
51456         view.render();
51457
51458         if(this.loadMask){
51459             this.loadMask = new Roo.LoadMask(this.container,
51460                     Roo.apply({store:this.dataSource}, this.loadMask));
51461         }
51462         
51463         
51464         if (this.toolbar && this.toolbar.xtype) {
51465             this.toolbar.container = this.getView().getHeaderPanel(true);
51466             this.toolbar = new Roo.Toolbar(this.toolbar);
51467         }
51468         if (this.footer && this.footer.xtype) {
51469             this.footer.dataSource = this.getDataSource();
51470             this.footer.container = this.getView().getFooterPanel(true);
51471             this.footer = Roo.factory(this.footer, Roo);
51472         }
51473         if (this.dropTarget && this.dropTarget.xtype) {
51474             delete this.dropTarget.xtype;
51475             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51476         }
51477         
51478         
51479         this.rendered = true;
51480         this.fireEvent('render', this);
51481         return this;
51482     },
51483
51484         /**
51485          * Reconfigures the grid to use a different Store and Column Model.
51486          * The View will be bound to the new objects and refreshed.
51487          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51488          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51489          */
51490     reconfigure : function(dataSource, colModel){
51491         if(this.loadMask){
51492             this.loadMask.destroy();
51493             this.loadMask = new Roo.LoadMask(this.container,
51494                     Roo.apply({store:dataSource}, this.loadMask));
51495         }
51496         this.view.bind(dataSource, colModel);
51497         this.dataSource = dataSource;
51498         this.colModel = colModel;
51499         this.view.refresh(true);
51500     },
51501
51502     // private
51503     onKeyDown : function(e){
51504         this.fireEvent("keydown", e);
51505     },
51506
51507     /**
51508      * Destroy this grid.
51509      * @param {Boolean} removeEl True to remove the element
51510      */
51511     destroy : function(removeEl, keepListeners){
51512         if(this.loadMask){
51513             this.loadMask.destroy();
51514         }
51515         var c = this.container;
51516         c.removeAllListeners();
51517         this.view.destroy();
51518         this.colModel.purgeListeners();
51519         if(!keepListeners){
51520             this.purgeListeners();
51521         }
51522         c.update("");
51523         if(removeEl === true){
51524             c.remove();
51525         }
51526     },
51527
51528     // private
51529     processEvent : function(name, e){
51530         // does this fire select???
51531         Roo.log('grid:processEvent '  + name);
51532         
51533         if (name != 'touchstart' ) {
51534             this.fireEvent(name, e);    
51535         }
51536         
51537         var t = e.getTarget();
51538         var v = this.view;
51539         var header = v.findHeaderIndex(t);
51540         if(header !== false){
51541             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
51542         }else{
51543             var row = v.findRowIndex(t);
51544             var cell = v.findCellIndex(t);
51545             if (name == 'touchstart') {
51546                 // first touch is always a click.
51547                 // hopefull this happens after selection is updated.?
51548                 name = false;
51549                 
51550                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51551                     var cs = this.selModel.getSelectedCell();
51552                     if (row == cs[0] && cell == cs[1]){
51553                         name = 'dblclick';
51554                     }
51555                 }
51556                 if (typeof(this.selModel.getSelections) != 'undefined') {
51557                     var cs = this.selModel.getSelections();
51558                     var ds = this.dataSource;
51559                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51560                         name = 'dblclick';
51561                     }
51562                 }
51563                 if (!name) {
51564                     return;
51565                 }
51566             }
51567             
51568             
51569             if(row !== false){
51570                 this.fireEvent("row" + name, this, row, e);
51571                 if(cell !== false){
51572                     this.fireEvent("cell" + name, this, row, cell, e);
51573                 }
51574             }
51575         }
51576     },
51577
51578     // private
51579     onClick : function(e){
51580         this.processEvent("click", e);
51581     },
51582    // private
51583     onTouchStart : function(e){
51584         this.processEvent("touchstart", e);
51585     },
51586
51587     // private
51588     onContextMenu : function(e, t){
51589         this.processEvent("contextmenu", e);
51590     },
51591
51592     // private
51593     onDblClick : function(e){
51594         this.processEvent("dblclick", e);
51595     },
51596
51597     // private
51598     walkCells : function(row, col, step, fn, scope){
51599         var cm = this.colModel, clen = cm.getColumnCount();
51600         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51601         if(step < 0){
51602             if(col < 0){
51603                 row--;
51604                 first = false;
51605             }
51606             while(row >= 0){
51607                 if(!first){
51608                     col = clen-1;
51609                 }
51610                 first = false;
51611                 while(col >= 0){
51612                     if(fn.call(scope || this, row, col, cm) === true){
51613                         return [row, col];
51614                     }
51615                     col--;
51616                 }
51617                 row--;
51618             }
51619         } else {
51620             if(col >= clen){
51621                 row++;
51622                 first = false;
51623             }
51624             while(row < rlen){
51625                 if(!first){
51626                     col = 0;
51627                 }
51628                 first = false;
51629                 while(col < clen){
51630                     if(fn.call(scope || this, row, col, cm) === true){
51631                         return [row, col];
51632                     }
51633                     col++;
51634                 }
51635                 row++;
51636             }
51637         }
51638         return null;
51639     },
51640
51641     // private
51642     getSelections : function(){
51643         return this.selModel.getSelections();
51644     },
51645
51646     /**
51647      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51648      * but if manual update is required this method will initiate it.
51649      */
51650     autoSize : function(){
51651         if(this.rendered){
51652             this.view.layout();
51653             if(this.view.adjustForScroll){
51654                 this.view.adjustForScroll();
51655             }
51656         }
51657     },
51658
51659     /**
51660      * Returns the grid's underlying element.
51661      * @return {Element} The element
51662      */
51663     getGridEl : function(){
51664         return this.container;
51665     },
51666
51667     // private for compatibility, overridden by editor grid
51668     stopEditing : function(){},
51669
51670     /**
51671      * Returns the grid's SelectionModel.
51672      * @return {SelectionModel}
51673      */
51674     getSelectionModel : function(){
51675         if(!this.selModel){
51676             this.selModel = new Roo.grid.RowSelectionModel();
51677         }
51678         return this.selModel;
51679     },
51680
51681     /**
51682      * Returns the grid's DataSource.
51683      * @return {DataSource}
51684      */
51685     getDataSource : function(){
51686         return this.dataSource;
51687     },
51688
51689     /**
51690      * Returns the grid's ColumnModel.
51691      * @return {ColumnModel}
51692      */
51693     getColumnModel : function(){
51694         return this.colModel;
51695     },
51696
51697     /**
51698      * Returns the grid's GridView object.
51699      * @return {GridView}
51700      */
51701     getView : function(){
51702         if(!this.view){
51703             this.view = new Roo.grid.GridView(this.viewConfig);
51704         }
51705         return this.view;
51706     },
51707     /**
51708      * Called to get grid's drag proxy text, by default returns this.ddText.
51709      * @return {String}
51710      */
51711     getDragDropText : function(){
51712         var count = this.selModel.getCount();
51713         return String.format(this.ddText, count, count == 1 ? '' : 's');
51714     }
51715 });
51716 /**
51717  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51718  * %0 is replaced with the number of selected rows.
51719  * @type String
51720  */
51721 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51722  * Based on:
51723  * Ext JS Library 1.1.1
51724  * Copyright(c) 2006-2007, Ext JS, LLC.
51725  *
51726  * Originally Released Under LGPL - original licence link has changed is not relivant.
51727  *
51728  * Fork - LGPL
51729  * <script type="text/javascript">
51730  */
51731  
51732 Roo.grid.AbstractGridView = function(){
51733         this.grid = null;
51734         
51735         this.events = {
51736             "beforerowremoved" : true,
51737             "beforerowsinserted" : true,
51738             "beforerefresh" : true,
51739             "rowremoved" : true,
51740             "rowsinserted" : true,
51741             "rowupdated" : true,
51742             "refresh" : true
51743         };
51744     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51745 };
51746
51747 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51748     rowClass : "x-grid-row",
51749     cellClass : "x-grid-cell",
51750     tdClass : "x-grid-td",
51751     hdClass : "x-grid-hd",
51752     splitClass : "x-grid-hd-split",
51753     
51754         init: function(grid){
51755         this.grid = grid;
51756                 var cid = this.grid.getGridEl().id;
51757         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51758         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51759         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51760         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51761         },
51762         
51763         getColumnRenderers : function(){
51764         var renderers = [];
51765         var cm = this.grid.colModel;
51766         var colCount = cm.getColumnCount();
51767         for(var i = 0; i < colCount; i++){
51768             renderers[i] = cm.getRenderer(i);
51769         }
51770         return renderers;
51771     },
51772     
51773     getColumnIds : function(){
51774         var ids = [];
51775         var cm = this.grid.colModel;
51776         var colCount = cm.getColumnCount();
51777         for(var i = 0; i < colCount; i++){
51778             ids[i] = cm.getColumnId(i);
51779         }
51780         return ids;
51781     },
51782     
51783     getDataIndexes : function(){
51784         if(!this.indexMap){
51785             this.indexMap = this.buildIndexMap();
51786         }
51787         return this.indexMap.colToData;
51788     },
51789     
51790     getColumnIndexByDataIndex : function(dataIndex){
51791         if(!this.indexMap){
51792             this.indexMap = this.buildIndexMap();
51793         }
51794         return this.indexMap.dataToCol[dataIndex];
51795     },
51796     
51797     /**
51798      * Set a css style for a column dynamically. 
51799      * @param {Number} colIndex The index of the column
51800      * @param {String} name The css property name
51801      * @param {String} value The css value
51802      */
51803     setCSSStyle : function(colIndex, name, value){
51804         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51805         Roo.util.CSS.updateRule(selector, name, value);
51806     },
51807     
51808     generateRules : function(cm){
51809         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51810         Roo.util.CSS.removeStyleSheet(rulesId);
51811         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51812             var cid = cm.getColumnId(i);
51813             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51814                          this.tdSelector, cid, " {\n}\n",
51815                          this.hdSelector, cid, " {\n}\n",
51816                          this.splitSelector, cid, " {\n}\n");
51817         }
51818         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51819     }
51820 });/*
51821  * Based on:
51822  * Ext JS Library 1.1.1
51823  * Copyright(c) 2006-2007, Ext JS, LLC.
51824  *
51825  * Originally Released Under LGPL - original licence link has changed is not relivant.
51826  *
51827  * Fork - LGPL
51828  * <script type="text/javascript">
51829  */
51830
51831 // private
51832 // This is a support class used internally by the Grid components
51833 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51834     this.grid = grid;
51835     this.view = grid.getView();
51836     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51837     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51838     if(hd2){
51839         this.setHandleElId(Roo.id(hd));
51840         this.setOuterHandleElId(Roo.id(hd2));
51841     }
51842     this.scroll = false;
51843 };
51844 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51845     maxDragWidth: 120,
51846     getDragData : function(e){
51847         var t = Roo.lib.Event.getTarget(e);
51848         var h = this.view.findHeaderCell(t);
51849         if(h){
51850             return {ddel: h.firstChild, header:h};
51851         }
51852         return false;
51853     },
51854
51855     onInitDrag : function(e){
51856         this.view.headersDisabled = true;
51857         var clone = this.dragData.ddel.cloneNode(true);
51858         clone.id = Roo.id();
51859         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51860         this.proxy.update(clone);
51861         return true;
51862     },
51863
51864     afterValidDrop : function(){
51865         var v = this.view;
51866         setTimeout(function(){
51867             v.headersDisabled = false;
51868         }, 50);
51869     },
51870
51871     afterInvalidDrop : function(){
51872         var v = this.view;
51873         setTimeout(function(){
51874             v.headersDisabled = false;
51875         }, 50);
51876     }
51877 });
51878 /*
51879  * Based on:
51880  * Ext JS Library 1.1.1
51881  * Copyright(c) 2006-2007, Ext JS, LLC.
51882  *
51883  * Originally Released Under LGPL - original licence link has changed is not relivant.
51884  *
51885  * Fork - LGPL
51886  * <script type="text/javascript">
51887  */
51888 // private
51889 // This is a support class used internally by the Grid components
51890 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51891     this.grid = grid;
51892     this.view = grid.getView();
51893     // split the proxies so they don't interfere with mouse events
51894     this.proxyTop = Roo.DomHelper.append(document.body, {
51895         cls:"col-move-top", html:"&#160;"
51896     }, true);
51897     this.proxyBottom = Roo.DomHelper.append(document.body, {
51898         cls:"col-move-bottom", html:"&#160;"
51899     }, true);
51900     this.proxyTop.hide = this.proxyBottom.hide = function(){
51901         this.setLeftTop(-100,-100);
51902         this.setStyle("visibility", "hidden");
51903     };
51904     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51905     // temporarily disabled
51906     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51907     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51908 };
51909 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51910     proxyOffsets : [-4, -9],
51911     fly: Roo.Element.fly,
51912
51913     getTargetFromEvent : function(e){
51914         var t = Roo.lib.Event.getTarget(e);
51915         var cindex = this.view.findCellIndex(t);
51916         if(cindex !== false){
51917             return this.view.getHeaderCell(cindex);
51918         }
51919         return null;
51920     },
51921
51922     nextVisible : function(h){
51923         var v = this.view, cm = this.grid.colModel;
51924         h = h.nextSibling;
51925         while(h){
51926             if(!cm.isHidden(v.getCellIndex(h))){
51927                 return h;
51928             }
51929             h = h.nextSibling;
51930         }
51931         return null;
51932     },
51933
51934     prevVisible : function(h){
51935         var v = this.view, cm = this.grid.colModel;
51936         h = h.prevSibling;
51937         while(h){
51938             if(!cm.isHidden(v.getCellIndex(h))){
51939                 return h;
51940             }
51941             h = h.prevSibling;
51942         }
51943         return null;
51944     },
51945
51946     positionIndicator : function(h, n, e){
51947         var x = Roo.lib.Event.getPageX(e);
51948         var r = Roo.lib.Dom.getRegion(n.firstChild);
51949         var px, pt, py = r.top + this.proxyOffsets[1];
51950         if((r.right - x) <= (r.right-r.left)/2){
51951             px = r.right+this.view.borderWidth;
51952             pt = "after";
51953         }else{
51954             px = r.left;
51955             pt = "before";
51956         }
51957         var oldIndex = this.view.getCellIndex(h);
51958         var newIndex = this.view.getCellIndex(n);
51959
51960         if(this.grid.colModel.isFixed(newIndex)){
51961             return false;
51962         }
51963
51964         var locked = this.grid.colModel.isLocked(newIndex);
51965
51966         if(pt == "after"){
51967             newIndex++;
51968         }
51969         if(oldIndex < newIndex){
51970             newIndex--;
51971         }
51972         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51973             return false;
51974         }
51975         px +=  this.proxyOffsets[0];
51976         this.proxyTop.setLeftTop(px, py);
51977         this.proxyTop.show();
51978         if(!this.bottomOffset){
51979             this.bottomOffset = this.view.mainHd.getHeight();
51980         }
51981         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51982         this.proxyBottom.show();
51983         return pt;
51984     },
51985
51986     onNodeEnter : function(n, dd, e, data){
51987         if(data.header != n){
51988             this.positionIndicator(data.header, n, e);
51989         }
51990     },
51991
51992     onNodeOver : function(n, dd, e, data){
51993         var result = false;
51994         if(data.header != n){
51995             result = this.positionIndicator(data.header, n, e);
51996         }
51997         if(!result){
51998             this.proxyTop.hide();
51999             this.proxyBottom.hide();
52000         }
52001         return result ? this.dropAllowed : this.dropNotAllowed;
52002     },
52003
52004     onNodeOut : function(n, dd, e, data){
52005         this.proxyTop.hide();
52006         this.proxyBottom.hide();
52007     },
52008
52009     onNodeDrop : function(n, dd, e, data){
52010         var h = data.header;
52011         if(h != n){
52012             var cm = this.grid.colModel;
52013             var x = Roo.lib.Event.getPageX(e);
52014             var r = Roo.lib.Dom.getRegion(n.firstChild);
52015             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52016             var oldIndex = this.view.getCellIndex(h);
52017             var newIndex = this.view.getCellIndex(n);
52018             var locked = cm.isLocked(newIndex);
52019             if(pt == "after"){
52020                 newIndex++;
52021             }
52022             if(oldIndex < newIndex){
52023                 newIndex--;
52024             }
52025             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52026                 return false;
52027             }
52028             cm.setLocked(oldIndex, locked, true);
52029             cm.moveColumn(oldIndex, newIndex);
52030             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52031             return true;
52032         }
52033         return false;
52034     }
52035 });
52036 /*
52037  * Based on:
52038  * Ext JS Library 1.1.1
52039  * Copyright(c) 2006-2007, Ext JS, LLC.
52040  *
52041  * Originally Released Under LGPL - original licence link has changed is not relivant.
52042  *
52043  * Fork - LGPL
52044  * <script type="text/javascript">
52045  */
52046   
52047 /**
52048  * @class Roo.grid.GridView
52049  * @extends Roo.util.Observable
52050  *
52051  * @constructor
52052  * @param {Object} config
52053  */
52054 Roo.grid.GridView = function(config){
52055     Roo.grid.GridView.superclass.constructor.call(this);
52056     this.el = null;
52057
52058     Roo.apply(this, config);
52059 };
52060
52061 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52062
52063     unselectable :  'unselectable="on"',
52064     unselectableCls :  'x-unselectable',
52065     
52066     
52067     rowClass : "x-grid-row",
52068
52069     cellClass : "x-grid-col",
52070
52071     tdClass : "x-grid-td",
52072
52073     hdClass : "x-grid-hd",
52074
52075     splitClass : "x-grid-split",
52076
52077     sortClasses : ["sort-asc", "sort-desc"],
52078
52079     enableMoveAnim : false,
52080
52081     hlColor: "C3DAF9",
52082
52083     dh : Roo.DomHelper,
52084
52085     fly : Roo.Element.fly,
52086
52087     css : Roo.util.CSS,
52088
52089     borderWidth: 1,
52090
52091     splitOffset: 3,
52092
52093     scrollIncrement : 22,
52094
52095     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52096
52097     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52098
52099     bind : function(ds, cm){
52100         if(this.ds){
52101             this.ds.un("load", this.onLoad, this);
52102             this.ds.un("datachanged", this.onDataChange, this);
52103             this.ds.un("add", this.onAdd, this);
52104             this.ds.un("remove", this.onRemove, this);
52105             this.ds.un("update", this.onUpdate, this);
52106             this.ds.un("clear", this.onClear, this);
52107         }
52108         if(ds){
52109             ds.on("load", this.onLoad, this);
52110             ds.on("datachanged", this.onDataChange, this);
52111             ds.on("add", this.onAdd, this);
52112             ds.on("remove", this.onRemove, this);
52113             ds.on("update", this.onUpdate, this);
52114             ds.on("clear", this.onClear, this);
52115         }
52116         this.ds = ds;
52117
52118         if(this.cm){
52119             this.cm.un("widthchange", this.onColWidthChange, this);
52120             this.cm.un("headerchange", this.onHeaderChange, this);
52121             this.cm.un("hiddenchange", this.onHiddenChange, this);
52122             this.cm.un("columnmoved", this.onColumnMove, this);
52123             this.cm.un("columnlockchange", this.onColumnLock, this);
52124         }
52125         if(cm){
52126             this.generateRules(cm);
52127             cm.on("widthchange", this.onColWidthChange, this);
52128             cm.on("headerchange", this.onHeaderChange, this);
52129             cm.on("hiddenchange", this.onHiddenChange, this);
52130             cm.on("columnmoved", this.onColumnMove, this);
52131             cm.on("columnlockchange", this.onColumnLock, this);
52132         }
52133         this.cm = cm;
52134     },
52135
52136     init: function(grid){
52137         Roo.grid.GridView.superclass.init.call(this, grid);
52138
52139         this.bind(grid.dataSource, grid.colModel);
52140
52141         grid.on("headerclick", this.handleHeaderClick, this);
52142
52143         if(grid.trackMouseOver){
52144             grid.on("mouseover", this.onRowOver, this);
52145             grid.on("mouseout", this.onRowOut, this);
52146         }
52147         grid.cancelTextSelection = function(){};
52148         this.gridId = grid.id;
52149
52150         var tpls = this.templates || {};
52151
52152         if(!tpls.master){
52153             tpls.master = new Roo.Template(
52154                '<div class="x-grid" hidefocus="true">',
52155                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52156                   '<div class="x-grid-topbar"></div>',
52157                   '<div class="x-grid-scroller"><div></div></div>',
52158                   '<div class="x-grid-locked">',
52159                       '<div class="x-grid-header">{lockedHeader}</div>',
52160                       '<div class="x-grid-body">{lockedBody}</div>',
52161                   "</div>",
52162                   '<div class="x-grid-viewport">',
52163                       '<div class="x-grid-header">{header}</div>',
52164                       '<div class="x-grid-body">{body}</div>',
52165                   "</div>",
52166                   '<div class="x-grid-bottombar"></div>',
52167                  
52168                   '<div class="x-grid-resize-proxy">&#160;</div>',
52169                "</div>"
52170             );
52171             tpls.master.disableformats = true;
52172         }
52173
52174         if(!tpls.header){
52175             tpls.header = new Roo.Template(
52176                '<table border="0" cellspacing="0" cellpadding="0">',
52177                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52178                "</table>{splits}"
52179             );
52180             tpls.header.disableformats = true;
52181         }
52182         tpls.header.compile();
52183
52184         if(!tpls.hcell){
52185             tpls.hcell = new Roo.Template(
52186                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52187                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52188                 "</div></td>"
52189              );
52190              tpls.hcell.disableFormats = true;
52191         }
52192         tpls.hcell.compile();
52193
52194         if(!tpls.hsplit){
52195             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52196                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52197             tpls.hsplit.disableFormats = true;
52198         }
52199         tpls.hsplit.compile();
52200
52201         if(!tpls.body){
52202             tpls.body = new Roo.Template(
52203                '<table border="0" cellspacing="0" cellpadding="0">',
52204                "<tbody>{rows}</tbody>",
52205                "</table>"
52206             );
52207             tpls.body.disableFormats = true;
52208         }
52209         tpls.body.compile();
52210
52211         if(!tpls.row){
52212             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52213             tpls.row.disableFormats = true;
52214         }
52215         tpls.row.compile();
52216
52217         if(!tpls.cell){
52218             tpls.cell = new Roo.Template(
52219                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52220                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52221                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52222                 "</td>"
52223             );
52224             tpls.cell.disableFormats = true;
52225         }
52226         tpls.cell.compile();
52227
52228         this.templates = tpls;
52229     },
52230
52231     // remap these for backwards compat
52232     onColWidthChange : function(){
52233         this.updateColumns.apply(this, arguments);
52234     },
52235     onHeaderChange : function(){
52236         this.updateHeaders.apply(this, arguments);
52237     }, 
52238     onHiddenChange : function(){
52239         this.handleHiddenChange.apply(this, arguments);
52240     },
52241     onColumnMove : function(){
52242         this.handleColumnMove.apply(this, arguments);
52243     },
52244     onColumnLock : function(){
52245         this.handleLockChange.apply(this, arguments);
52246     },
52247
52248     onDataChange : function(){
52249         this.refresh();
52250         this.updateHeaderSortState();
52251     },
52252
52253     onClear : function(){
52254         this.refresh();
52255     },
52256
52257     onUpdate : function(ds, record){
52258         this.refreshRow(record);
52259     },
52260
52261     refreshRow : function(record){
52262         var ds = this.ds, index;
52263         if(typeof record == 'number'){
52264             index = record;
52265             record = ds.getAt(index);
52266         }else{
52267             index = ds.indexOf(record);
52268         }
52269         this.insertRows(ds, index, index, true);
52270         this.onRemove(ds, record, index+1, true);
52271         this.syncRowHeights(index, index);
52272         this.layout();
52273         this.fireEvent("rowupdated", this, index, record);
52274     },
52275
52276     onAdd : function(ds, records, index){
52277         this.insertRows(ds, index, index + (records.length-1));
52278     },
52279
52280     onRemove : function(ds, record, index, isUpdate){
52281         if(isUpdate !== true){
52282             this.fireEvent("beforerowremoved", this, index, record);
52283         }
52284         var bt = this.getBodyTable(), lt = this.getLockedTable();
52285         if(bt.rows[index]){
52286             bt.firstChild.removeChild(bt.rows[index]);
52287         }
52288         if(lt.rows[index]){
52289             lt.firstChild.removeChild(lt.rows[index]);
52290         }
52291         if(isUpdate !== true){
52292             this.stripeRows(index);
52293             this.syncRowHeights(index, index);
52294             this.layout();
52295             this.fireEvent("rowremoved", this, index, record);
52296         }
52297     },
52298
52299     onLoad : function(){
52300         this.scrollToTop();
52301     },
52302
52303     /**
52304      * Scrolls the grid to the top
52305      */
52306     scrollToTop : function(){
52307         if(this.scroller){
52308             this.scroller.dom.scrollTop = 0;
52309             this.syncScroll();
52310         }
52311     },
52312
52313     /**
52314      * Gets a panel in the header of the grid that can be used for toolbars etc.
52315      * After modifying the contents of this panel a call to grid.autoSize() may be
52316      * required to register any changes in size.
52317      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52318      * @return Roo.Element
52319      */
52320     getHeaderPanel : function(doShow){
52321         if(doShow){
52322             this.headerPanel.show();
52323         }
52324         return this.headerPanel;
52325     },
52326
52327     /**
52328      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52329      * After modifying the contents of this panel a call to grid.autoSize() may be
52330      * required to register any changes in size.
52331      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52332      * @return Roo.Element
52333      */
52334     getFooterPanel : function(doShow){
52335         if(doShow){
52336             this.footerPanel.show();
52337         }
52338         return this.footerPanel;
52339     },
52340
52341     initElements : function(){
52342         var E = Roo.Element;
52343         var el = this.grid.getGridEl().dom.firstChild;
52344         var cs = el.childNodes;
52345
52346         this.el = new E(el);
52347         
52348          this.focusEl = new E(el.firstChild);
52349         this.focusEl.swallowEvent("click", true);
52350         
52351         this.headerPanel = new E(cs[1]);
52352         this.headerPanel.enableDisplayMode("block");
52353
52354         this.scroller = new E(cs[2]);
52355         this.scrollSizer = new E(this.scroller.dom.firstChild);
52356
52357         this.lockedWrap = new E(cs[3]);
52358         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52359         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52360
52361         this.mainWrap = new E(cs[4]);
52362         this.mainHd = new E(this.mainWrap.dom.firstChild);
52363         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52364
52365         this.footerPanel = new E(cs[5]);
52366         this.footerPanel.enableDisplayMode("block");
52367
52368         this.resizeProxy = new E(cs[6]);
52369
52370         this.headerSelector = String.format(
52371            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52372            this.lockedHd.id, this.mainHd.id
52373         );
52374
52375         this.splitterSelector = String.format(
52376            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52377            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52378         );
52379     },
52380     idToCssName : function(s)
52381     {
52382         return s.replace(/[^a-z0-9]+/ig, '-');
52383     },
52384
52385     getHeaderCell : function(index){
52386         return Roo.DomQuery.select(this.headerSelector)[index];
52387     },
52388
52389     getHeaderCellMeasure : function(index){
52390         return this.getHeaderCell(index).firstChild;
52391     },
52392
52393     getHeaderCellText : function(index){
52394         return this.getHeaderCell(index).firstChild.firstChild;
52395     },
52396
52397     getLockedTable : function(){
52398         return this.lockedBody.dom.firstChild;
52399     },
52400
52401     getBodyTable : function(){
52402         return this.mainBody.dom.firstChild;
52403     },
52404
52405     getLockedRow : function(index){
52406         return this.getLockedTable().rows[index];
52407     },
52408
52409     getRow : function(index){
52410         return this.getBodyTable().rows[index];
52411     },
52412
52413     getRowComposite : function(index){
52414         if(!this.rowEl){
52415             this.rowEl = new Roo.CompositeElementLite();
52416         }
52417         var els = [], lrow, mrow;
52418         if(lrow = this.getLockedRow(index)){
52419             els.push(lrow);
52420         }
52421         if(mrow = this.getRow(index)){
52422             els.push(mrow);
52423         }
52424         this.rowEl.elements = els;
52425         return this.rowEl;
52426     },
52427     /**
52428      * Gets the 'td' of the cell
52429      * 
52430      * @param {Integer} rowIndex row to select
52431      * @param {Integer} colIndex column to select
52432      * 
52433      * @return {Object} 
52434      */
52435     getCell : function(rowIndex, colIndex){
52436         var locked = this.cm.getLockedCount();
52437         var source;
52438         if(colIndex < locked){
52439             source = this.lockedBody.dom.firstChild;
52440         }else{
52441             source = this.mainBody.dom.firstChild;
52442             colIndex -= locked;
52443         }
52444         return source.rows[rowIndex].childNodes[colIndex];
52445     },
52446
52447     getCellText : function(rowIndex, colIndex){
52448         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52449     },
52450
52451     getCellBox : function(cell){
52452         var b = this.fly(cell).getBox();
52453         if(Roo.isOpera){ // opera fails to report the Y
52454             b.y = cell.offsetTop + this.mainBody.getY();
52455         }
52456         return b;
52457     },
52458
52459     getCellIndex : function(cell){
52460         var id = String(cell.className).match(this.cellRE);
52461         if(id){
52462             return parseInt(id[1], 10);
52463         }
52464         return 0;
52465     },
52466
52467     findHeaderIndex : function(n){
52468         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52469         return r ? this.getCellIndex(r) : false;
52470     },
52471
52472     findHeaderCell : function(n){
52473         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52474         return r ? r : false;
52475     },
52476
52477     findRowIndex : function(n){
52478         if(!n){
52479             return false;
52480         }
52481         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52482         return r ? r.rowIndex : false;
52483     },
52484
52485     findCellIndex : function(node){
52486         var stop = this.el.dom;
52487         while(node && node != stop){
52488             if(this.findRE.test(node.className)){
52489                 return this.getCellIndex(node);
52490             }
52491             node = node.parentNode;
52492         }
52493         return false;
52494     },
52495
52496     getColumnId : function(index){
52497         return this.cm.getColumnId(index);
52498     },
52499
52500     getSplitters : function()
52501     {
52502         if(this.splitterSelector){
52503            return Roo.DomQuery.select(this.splitterSelector);
52504         }else{
52505             return null;
52506       }
52507     },
52508
52509     getSplitter : function(index){
52510         return this.getSplitters()[index];
52511     },
52512
52513     onRowOver : function(e, t){
52514         var row;
52515         if((row = this.findRowIndex(t)) !== false){
52516             this.getRowComposite(row).addClass("x-grid-row-over");
52517         }
52518     },
52519
52520     onRowOut : function(e, t){
52521         var row;
52522         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52523             this.getRowComposite(row).removeClass("x-grid-row-over");
52524         }
52525     },
52526
52527     renderHeaders : function(){
52528         var cm = this.cm;
52529         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52530         var cb = [], lb = [], sb = [], lsb = [], p = {};
52531         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52532             p.cellId = "x-grid-hd-0-" + i;
52533             p.splitId = "x-grid-csplit-0-" + i;
52534             p.id = cm.getColumnId(i);
52535             p.title = cm.getColumnTooltip(i) || "";
52536             p.value = cm.getColumnHeader(i) || "";
52537             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52538             if(!cm.isLocked(i)){
52539                 cb[cb.length] = ct.apply(p);
52540                 sb[sb.length] = st.apply(p);
52541             }else{
52542                 lb[lb.length] = ct.apply(p);
52543                 lsb[lsb.length] = st.apply(p);
52544             }
52545         }
52546         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52547                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52548     },
52549
52550     updateHeaders : function(){
52551         var html = this.renderHeaders();
52552         this.lockedHd.update(html[0]);
52553         this.mainHd.update(html[1]);
52554     },
52555
52556     /**
52557      * Focuses the specified row.
52558      * @param {Number} row The row index
52559      */
52560     focusRow : function(row)
52561     {
52562         //Roo.log('GridView.focusRow');
52563         var x = this.scroller.dom.scrollLeft;
52564         this.focusCell(row, 0, false);
52565         this.scroller.dom.scrollLeft = x;
52566     },
52567
52568     /**
52569      * Focuses the specified cell.
52570      * @param {Number} row The row index
52571      * @param {Number} col The column index
52572      * @param {Boolean} hscroll false to disable horizontal scrolling
52573      */
52574     focusCell : function(row, col, hscroll)
52575     {
52576         //Roo.log('GridView.focusCell');
52577         var el = this.ensureVisible(row, col, hscroll);
52578         this.focusEl.alignTo(el, "tl-tl");
52579         if(Roo.isGecko){
52580             this.focusEl.focus();
52581         }else{
52582             this.focusEl.focus.defer(1, this.focusEl);
52583         }
52584     },
52585
52586     /**
52587      * Scrolls the specified cell into view
52588      * @param {Number} row The row index
52589      * @param {Number} col The column index
52590      * @param {Boolean} hscroll false to disable horizontal scrolling
52591      */
52592     ensureVisible : function(row, col, hscroll)
52593     {
52594         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52595         //return null; //disable for testing.
52596         if(typeof row != "number"){
52597             row = row.rowIndex;
52598         }
52599         if(row < 0 && row >= this.ds.getCount()){
52600             return  null;
52601         }
52602         col = (col !== undefined ? col : 0);
52603         var cm = this.grid.colModel;
52604         while(cm.isHidden(col)){
52605             col++;
52606         }
52607
52608         var el = this.getCell(row, col);
52609         if(!el){
52610             return null;
52611         }
52612         var c = this.scroller.dom;
52613
52614         var ctop = parseInt(el.offsetTop, 10);
52615         var cleft = parseInt(el.offsetLeft, 10);
52616         var cbot = ctop + el.offsetHeight;
52617         var cright = cleft + el.offsetWidth;
52618         
52619         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52620         var stop = parseInt(c.scrollTop, 10);
52621         var sleft = parseInt(c.scrollLeft, 10);
52622         var sbot = stop + ch;
52623         var sright = sleft + c.clientWidth;
52624         /*
52625         Roo.log('GridView.ensureVisible:' +
52626                 ' ctop:' + ctop +
52627                 ' c.clientHeight:' + c.clientHeight +
52628                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52629                 ' stop:' + stop +
52630                 ' cbot:' + cbot +
52631                 ' sbot:' + sbot +
52632                 ' ch:' + ch  
52633                 );
52634         */
52635         if(ctop < stop){
52636              c.scrollTop = ctop;
52637             //Roo.log("set scrolltop to ctop DISABLE?");
52638         }else if(cbot > sbot){
52639             //Roo.log("set scrolltop to cbot-ch");
52640             c.scrollTop = cbot-ch;
52641         }
52642         
52643         if(hscroll !== false){
52644             if(cleft < sleft){
52645                 c.scrollLeft = cleft;
52646             }else if(cright > sright){
52647                 c.scrollLeft = cright-c.clientWidth;
52648             }
52649         }
52650          
52651         return el;
52652     },
52653
52654     updateColumns : function(){
52655         this.grid.stopEditing();
52656         var cm = this.grid.colModel, colIds = this.getColumnIds();
52657         //var totalWidth = cm.getTotalWidth();
52658         var pos = 0;
52659         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52660             //if(cm.isHidden(i)) continue;
52661             var w = cm.getColumnWidth(i);
52662             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52663             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52664         }
52665         this.updateSplitters();
52666     },
52667
52668     generateRules : function(cm){
52669         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52670         Roo.util.CSS.removeStyleSheet(rulesId);
52671         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52672             var cid = cm.getColumnId(i);
52673             var align = '';
52674             if(cm.config[i].align){
52675                 align = 'text-align:'+cm.config[i].align+';';
52676             }
52677             var hidden = '';
52678             if(cm.isHidden(i)){
52679                 hidden = 'display:none;';
52680             }
52681             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52682             ruleBuf.push(
52683                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52684                     this.hdSelector, cid, " {\n", align, width, "}\n",
52685                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52686                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52687         }
52688         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52689     },
52690
52691     updateSplitters : function(){
52692         var cm = this.cm, s = this.getSplitters();
52693         if(s){ // splitters not created yet
52694             var pos = 0, locked = true;
52695             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52696                 if(cm.isHidden(i)) continue;
52697                 var w = cm.getColumnWidth(i); // make sure it's a number
52698                 if(!cm.isLocked(i) && locked){
52699                     pos = 0;
52700                     locked = false;
52701                 }
52702                 pos += w;
52703                 s[i].style.left = (pos-this.splitOffset) + "px";
52704             }
52705         }
52706     },
52707
52708     handleHiddenChange : function(colModel, colIndex, hidden){
52709         if(hidden){
52710             this.hideColumn(colIndex);
52711         }else{
52712             this.unhideColumn(colIndex);
52713         }
52714     },
52715
52716     hideColumn : function(colIndex){
52717         var cid = this.getColumnId(colIndex);
52718         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52719         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52720         if(Roo.isSafari){
52721             this.updateHeaders();
52722         }
52723         this.updateSplitters();
52724         this.layout();
52725     },
52726
52727     unhideColumn : function(colIndex){
52728         var cid = this.getColumnId(colIndex);
52729         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52730         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52731
52732         if(Roo.isSafari){
52733             this.updateHeaders();
52734         }
52735         this.updateSplitters();
52736         this.layout();
52737     },
52738
52739     insertRows : function(dm, firstRow, lastRow, isUpdate){
52740         if(firstRow == 0 && lastRow == dm.getCount()-1){
52741             this.refresh();
52742         }else{
52743             if(!isUpdate){
52744                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52745             }
52746             var s = this.getScrollState();
52747             var markup = this.renderRows(firstRow, lastRow);
52748             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52749             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52750             this.restoreScroll(s);
52751             if(!isUpdate){
52752                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52753                 this.syncRowHeights(firstRow, lastRow);
52754                 this.stripeRows(firstRow);
52755                 this.layout();
52756             }
52757         }
52758     },
52759
52760     bufferRows : function(markup, target, index){
52761         var before = null, trows = target.rows, tbody = target.tBodies[0];
52762         if(index < trows.length){
52763             before = trows[index];
52764         }
52765         var b = document.createElement("div");
52766         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52767         var rows = b.firstChild.rows;
52768         for(var i = 0, len = rows.length; i < len; i++){
52769             if(before){
52770                 tbody.insertBefore(rows[0], before);
52771             }else{
52772                 tbody.appendChild(rows[0]);
52773             }
52774         }
52775         b.innerHTML = "";
52776         b = null;
52777     },
52778
52779     deleteRows : function(dm, firstRow, lastRow){
52780         if(dm.getRowCount()<1){
52781             this.fireEvent("beforerefresh", this);
52782             this.mainBody.update("");
52783             this.lockedBody.update("");
52784             this.fireEvent("refresh", this);
52785         }else{
52786             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52787             var bt = this.getBodyTable();
52788             var tbody = bt.firstChild;
52789             var rows = bt.rows;
52790             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52791                 tbody.removeChild(rows[firstRow]);
52792             }
52793             this.stripeRows(firstRow);
52794             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52795         }
52796     },
52797
52798     updateRows : function(dataSource, firstRow, lastRow){
52799         var s = this.getScrollState();
52800         this.refresh();
52801         this.restoreScroll(s);
52802     },
52803
52804     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52805         if(!noRefresh){
52806            this.refresh();
52807         }
52808         this.updateHeaderSortState();
52809     },
52810
52811     getScrollState : function(){
52812         
52813         var sb = this.scroller.dom;
52814         return {left: sb.scrollLeft, top: sb.scrollTop};
52815     },
52816
52817     stripeRows : function(startRow){
52818         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52819             return;
52820         }
52821         startRow = startRow || 0;
52822         var rows = this.getBodyTable().rows;
52823         var lrows = this.getLockedTable().rows;
52824         var cls = ' x-grid-row-alt ';
52825         for(var i = startRow, len = rows.length; i < len; i++){
52826             var row = rows[i], lrow = lrows[i];
52827             var isAlt = ((i+1) % 2 == 0);
52828             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52829             if(isAlt == hasAlt){
52830                 continue;
52831             }
52832             if(isAlt){
52833                 row.className += " x-grid-row-alt";
52834             }else{
52835                 row.className = row.className.replace("x-grid-row-alt", "");
52836             }
52837             if(lrow){
52838                 lrow.className = row.className;
52839             }
52840         }
52841     },
52842
52843     restoreScroll : function(state){
52844         //Roo.log('GridView.restoreScroll');
52845         var sb = this.scroller.dom;
52846         sb.scrollLeft = state.left;
52847         sb.scrollTop = state.top;
52848         this.syncScroll();
52849     },
52850
52851     syncScroll : function(){
52852         //Roo.log('GridView.syncScroll');
52853         var sb = this.scroller.dom;
52854         var sh = this.mainHd.dom;
52855         var bs = this.mainBody.dom;
52856         var lv = this.lockedBody.dom;
52857         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52858         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52859     },
52860
52861     handleScroll : function(e){
52862         this.syncScroll();
52863         var sb = this.scroller.dom;
52864         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52865         e.stopEvent();
52866     },
52867
52868     handleWheel : function(e){
52869         var d = e.getWheelDelta();
52870         this.scroller.dom.scrollTop -= d*22;
52871         // set this here to prevent jumpy scrolling on large tables
52872         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52873         e.stopEvent();
52874     },
52875
52876     renderRows : function(startRow, endRow){
52877         // pull in all the crap needed to render rows
52878         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52879         var colCount = cm.getColumnCount();
52880
52881         if(ds.getCount() < 1){
52882             return ["", ""];
52883         }
52884
52885         // build a map for all the columns
52886         var cs = [];
52887         for(var i = 0; i < colCount; i++){
52888             var name = cm.getDataIndex(i);
52889             cs[i] = {
52890                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52891                 renderer : cm.getRenderer(i),
52892                 id : cm.getColumnId(i),
52893                 locked : cm.isLocked(i)
52894             };
52895         }
52896
52897         startRow = startRow || 0;
52898         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52899
52900         // records to render
52901         var rs = ds.getRange(startRow, endRow);
52902
52903         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52904     },
52905
52906     // As much as I hate to duplicate code, this was branched because FireFox really hates
52907     // [].join("") on strings. The performance difference was substantial enough to
52908     // branch this function
52909     doRender : Roo.isGecko ?
52910             function(cs, rs, ds, startRow, colCount, stripe){
52911                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52912                 // buffers
52913                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52914                 
52915                 var hasListener = this.grid.hasListener('rowclass');
52916                 var rowcfg = {};
52917                 for(var j = 0, len = rs.length; j < len; j++){
52918                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52919                     for(var i = 0; i < colCount; i++){
52920                         c = cs[i];
52921                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52922                         p.id = c.id;
52923                         p.css = p.attr = "";
52924                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52925                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52926                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52927                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52928                         }
52929                         var markup = ct.apply(p);
52930                         if(!c.locked){
52931                             cb+= markup;
52932                         }else{
52933                             lcb+= markup;
52934                         }
52935                     }
52936                     var alt = [];
52937                     if(stripe && ((rowIndex+1) % 2 == 0)){
52938                         alt.push("x-grid-row-alt")
52939                     }
52940                     if(r.dirty){
52941                         alt.push(  " x-grid-dirty-row");
52942                     }
52943                     rp.cells = lcb;
52944                     if(this.getRowClass){
52945                         alt.push(this.getRowClass(r, rowIndex));
52946                     }
52947                     if (hasListener) {
52948                         rowcfg = {
52949                              
52950                             record: r,
52951                             rowIndex : rowIndex,
52952                             rowClass : ''
52953                         }
52954                         this.grid.fireEvent('rowclass', this, rowcfg);
52955                         alt.push(rowcfg.rowClass);
52956                     }
52957                     rp.alt = alt.join(" ");
52958                     lbuf+= rt.apply(rp);
52959                     rp.cells = cb;
52960                     buf+=  rt.apply(rp);
52961                 }
52962                 return [lbuf, buf];
52963             } :
52964             function(cs, rs, ds, startRow, colCount, stripe){
52965                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52966                 // buffers
52967                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52968                 var hasListener = this.grid.hasListener('rowclass');
52969  
52970                 var rowcfg = {};
52971                 for(var j = 0, len = rs.length; j < len; j++){
52972                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52973                     for(var i = 0; i < colCount; i++){
52974                         c = cs[i];
52975                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52976                         p.id = c.id;
52977                         p.css = p.attr = "";
52978                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52979                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52980                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52981                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52982                         }
52983                         
52984                         var markup = ct.apply(p);
52985                         if(!c.locked){
52986                             cb[cb.length] = markup;
52987                         }else{
52988                             lcb[lcb.length] = markup;
52989                         }
52990                     }
52991                     var alt = [];
52992                     if(stripe && ((rowIndex+1) % 2 == 0)){
52993                         alt.push( "x-grid-row-alt");
52994                     }
52995                     if(r.dirty){
52996                         alt.push(" x-grid-dirty-row");
52997                     }
52998                     rp.cells = lcb;
52999                     if(this.getRowClass){
53000                         alt.push( this.getRowClass(r, rowIndex));
53001                     }
53002                     if (hasListener) {
53003                         rowcfg = {
53004                              
53005                             record: r,
53006                             rowIndex : rowIndex,
53007                             rowClass : ''
53008                         }
53009                         this.grid.fireEvent('rowclass', this, rowcfg);
53010                         alt.push(rowcfg.rowClass);
53011                     }
53012                     rp.alt = alt.join(" ");
53013                     rp.cells = lcb.join("");
53014                     lbuf[lbuf.length] = rt.apply(rp);
53015                     rp.cells = cb.join("");
53016                     buf[buf.length] =  rt.apply(rp);
53017                 }
53018                 return [lbuf.join(""), buf.join("")];
53019             },
53020
53021     renderBody : function(){
53022         var markup = this.renderRows();
53023         var bt = this.templates.body;
53024         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53025     },
53026
53027     /**
53028      * Refreshes the grid
53029      * @param {Boolean} headersToo
53030      */
53031     refresh : function(headersToo){
53032         this.fireEvent("beforerefresh", this);
53033         this.grid.stopEditing();
53034         var result = this.renderBody();
53035         this.lockedBody.update(result[0]);
53036         this.mainBody.update(result[1]);
53037         if(headersToo === true){
53038             this.updateHeaders();
53039             this.updateColumns();
53040             this.updateSplitters();
53041             this.updateHeaderSortState();
53042         }
53043         this.syncRowHeights();
53044         this.layout();
53045         this.fireEvent("refresh", this);
53046     },
53047
53048     handleColumnMove : function(cm, oldIndex, newIndex){
53049         this.indexMap = null;
53050         var s = this.getScrollState();
53051         this.refresh(true);
53052         this.restoreScroll(s);
53053         this.afterMove(newIndex);
53054     },
53055
53056     afterMove : function(colIndex){
53057         if(this.enableMoveAnim && Roo.enableFx){
53058             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53059         }
53060         // if multisort - fix sortOrder, and reload..
53061         if (this.grid.dataSource.multiSort) {
53062             // the we can call sort again..
53063             var dm = this.grid.dataSource;
53064             var cm = this.grid.colModel;
53065             var so = [];
53066             for(var i = 0; i < cm.config.length; i++ ) {
53067                 
53068                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53069                     continue; // dont' bother, it's not in sort list or being set.
53070                 }
53071                 
53072                 so.push(cm.config[i].dataIndex);
53073             };
53074             dm.sortOrder = so;
53075             dm.load(dm.lastOptions);
53076             
53077             
53078         }
53079         
53080     },
53081
53082     updateCell : function(dm, rowIndex, dataIndex){
53083         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53084         if(typeof colIndex == "undefined"){ // not present in grid
53085             return;
53086         }
53087         var cm = this.grid.colModel;
53088         var cell = this.getCell(rowIndex, colIndex);
53089         var cellText = this.getCellText(rowIndex, colIndex);
53090
53091         var p = {
53092             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53093             id : cm.getColumnId(colIndex),
53094             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53095         };
53096         var renderer = cm.getRenderer(colIndex);
53097         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53098         if(typeof val == "undefined" || val === "") val = "&#160;";
53099         cellText.innerHTML = val;
53100         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53101         this.syncRowHeights(rowIndex, rowIndex);
53102     },
53103
53104     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53105         var maxWidth = 0;
53106         if(this.grid.autoSizeHeaders){
53107             var h = this.getHeaderCellMeasure(colIndex);
53108             maxWidth = Math.max(maxWidth, h.scrollWidth);
53109         }
53110         var tb, index;
53111         if(this.cm.isLocked(colIndex)){
53112             tb = this.getLockedTable();
53113             index = colIndex;
53114         }else{
53115             tb = this.getBodyTable();
53116             index = colIndex - this.cm.getLockedCount();
53117         }
53118         if(tb && tb.rows){
53119             var rows = tb.rows;
53120             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53121             for(var i = 0; i < stopIndex; i++){
53122                 var cell = rows[i].childNodes[index].firstChild;
53123                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53124             }
53125         }
53126         return maxWidth + /*margin for error in IE*/ 5;
53127     },
53128     /**
53129      * Autofit a column to its content.
53130      * @param {Number} colIndex
53131      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53132      */
53133      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53134          if(this.cm.isHidden(colIndex)){
53135              return; // can't calc a hidden column
53136          }
53137         if(forceMinSize){
53138             var cid = this.cm.getColumnId(colIndex);
53139             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53140            if(this.grid.autoSizeHeaders){
53141                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53142            }
53143         }
53144         var newWidth = this.calcColumnWidth(colIndex);
53145         this.cm.setColumnWidth(colIndex,
53146             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53147         if(!suppressEvent){
53148             this.grid.fireEvent("columnresize", colIndex, newWidth);
53149         }
53150     },
53151
53152     /**
53153      * Autofits all columns to their content and then expands to fit any extra space in the grid
53154      */
53155      autoSizeColumns : function(){
53156         var cm = this.grid.colModel;
53157         var colCount = cm.getColumnCount();
53158         for(var i = 0; i < colCount; i++){
53159             this.autoSizeColumn(i, true, true);
53160         }
53161         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53162             this.fitColumns();
53163         }else{
53164             this.updateColumns();
53165             this.layout();
53166         }
53167     },
53168
53169     /**
53170      * Autofits all columns to the grid's width proportionate with their current size
53171      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53172      */
53173     fitColumns : function(reserveScrollSpace){
53174         var cm = this.grid.colModel;
53175         var colCount = cm.getColumnCount();
53176         var cols = [];
53177         var width = 0;
53178         var i, w;
53179         for (i = 0; i < colCount; i++){
53180             if(!cm.isHidden(i) && !cm.isFixed(i)){
53181                 w = cm.getColumnWidth(i);
53182                 cols.push(i);
53183                 cols.push(w);
53184                 width += w;
53185             }
53186         }
53187         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53188         if(reserveScrollSpace){
53189             avail -= 17;
53190         }
53191         var frac = (avail - cm.getTotalWidth())/width;
53192         while (cols.length){
53193             w = cols.pop();
53194             i = cols.pop();
53195             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53196         }
53197         this.updateColumns();
53198         this.layout();
53199     },
53200
53201     onRowSelect : function(rowIndex){
53202         var row = this.getRowComposite(rowIndex);
53203         row.addClass("x-grid-row-selected");
53204     },
53205
53206     onRowDeselect : function(rowIndex){
53207         var row = this.getRowComposite(rowIndex);
53208         row.removeClass("x-grid-row-selected");
53209     },
53210
53211     onCellSelect : function(row, col){
53212         var cell = this.getCell(row, col);
53213         if(cell){
53214             Roo.fly(cell).addClass("x-grid-cell-selected");
53215         }
53216     },
53217
53218     onCellDeselect : function(row, col){
53219         var cell = this.getCell(row, col);
53220         if(cell){
53221             Roo.fly(cell).removeClass("x-grid-cell-selected");
53222         }
53223     },
53224
53225     updateHeaderSortState : function(){
53226         
53227         // sort state can be single { field: xxx, direction : yyy}
53228         // or   { xxx=>ASC , yyy : DESC ..... }
53229         
53230         var mstate = {};
53231         if (!this.ds.multiSort) { 
53232             var state = this.ds.getSortState();
53233             if(!state){
53234                 return;
53235             }
53236             mstate[state.field] = state.direction;
53237             // FIXME... - this is not used here.. but might be elsewhere..
53238             this.sortState = state;
53239             
53240         } else {
53241             mstate = this.ds.sortToggle;
53242         }
53243         //remove existing sort classes..
53244         
53245         var sc = this.sortClasses;
53246         var hds = this.el.select(this.headerSelector).removeClass(sc);
53247         
53248         for(var f in mstate) {
53249         
53250             var sortColumn = this.cm.findColumnIndex(f);
53251             
53252             if(sortColumn != -1){
53253                 var sortDir = mstate[f];        
53254                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53255             }
53256         }
53257         
53258          
53259         
53260     },
53261
53262
53263     handleHeaderClick : function(g, index){
53264         if(this.headersDisabled){
53265             return;
53266         }
53267         var dm = g.dataSource, cm = g.colModel;
53268         if(!cm.isSortable(index)){
53269             return;
53270         }
53271         g.stopEditing();
53272         
53273         if (dm.multiSort) {
53274             // update the sortOrder
53275             var so = [];
53276             for(var i = 0; i < cm.config.length; i++ ) {
53277                 
53278                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53279                     continue; // dont' bother, it's not in sort list or being set.
53280                 }
53281                 
53282                 so.push(cm.config[i].dataIndex);
53283             };
53284             dm.sortOrder = so;
53285         }
53286         
53287         
53288         dm.sort(cm.getDataIndex(index));
53289     },
53290
53291
53292     destroy : function(){
53293         if(this.colMenu){
53294             this.colMenu.removeAll();
53295             Roo.menu.MenuMgr.unregister(this.colMenu);
53296             this.colMenu.getEl().remove();
53297             delete this.colMenu;
53298         }
53299         if(this.hmenu){
53300             this.hmenu.removeAll();
53301             Roo.menu.MenuMgr.unregister(this.hmenu);
53302             this.hmenu.getEl().remove();
53303             delete this.hmenu;
53304         }
53305         if(this.grid.enableColumnMove){
53306             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53307             if(dds){
53308                 for(var dd in dds){
53309                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53310                         var elid = dds[dd].dragElId;
53311                         dds[dd].unreg();
53312                         Roo.get(elid).remove();
53313                     } else if(dds[dd].config.isTarget){
53314                         dds[dd].proxyTop.remove();
53315                         dds[dd].proxyBottom.remove();
53316                         dds[dd].unreg();
53317                     }
53318                     if(Roo.dd.DDM.locationCache[dd]){
53319                         delete Roo.dd.DDM.locationCache[dd];
53320                     }
53321                 }
53322                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53323             }
53324         }
53325         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53326         this.bind(null, null);
53327         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53328     },
53329
53330     handleLockChange : function(){
53331         this.refresh(true);
53332     },
53333
53334     onDenyColumnLock : function(){
53335
53336     },
53337
53338     onDenyColumnHide : function(){
53339
53340     },
53341
53342     handleHdMenuClick : function(item){
53343         var index = this.hdCtxIndex;
53344         var cm = this.cm, ds = this.ds;
53345         switch(item.id){
53346             case "asc":
53347                 ds.sort(cm.getDataIndex(index), "ASC");
53348                 break;
53349             case "desc":
53350                 ds.sort(cm.getDataIndex(index), "DESC");
53351                 break;
53352             case "lock":
53353                 var lc = cm.getLockedCount();
53354                 if(cm.getColumnCount(true) <= lc+1){
53355                     this.onDenyColumnLock();
53356                     return;
53357                 }
53358                 if(lc != index){
53359                     cm.setLocked(index, true, true);
53360                     cm.moveColumn(index, lc);
53361                     this.grid.fireEvent("columnmove", index, lc);
53362                 }else{
53363                     cm.setLocked(index, true);
53364                 }
53365             break;
53366             case "unlock":
53367                 var lc = cm.getLockedCount();
53368                 if((lc-1) != index){
53369                     cm.setLocked(index, false, true);
53370                     cm.moveColumn(index, lc-1);
53371                     this.grid.fireEvent("columnmove", index, lc-1);
53372                 }else{
53373                     cm.setLocked(index, false);
53374                 }
53375             break;
53376             default:
53377                 index = cm.getIndexById(item.id.substr(4));
53378                 if(index != -1){
53379                     if(item.checked && cm.getColumnCount(true) <= 1){
53380                         this.onDenyColumnHide();
53381                         return false;
53382                     }
53383                     cm.setHidden(index, item.checked);
53384                 }
53385         }
53386         return true;
53387     },
53388
53389     beforeColMenuShow : function(){
53390         var cm = this.cm,  colCount = cm.getColumnCount();
53391         this.colMenu.removeAll();
53392         for(var i = 0; i < colCount; i++){
53393             this.colMenu.add(new Roo.menu.CheckItem({
53394                 id: "col-"+cm.getColumnId(i),
53395                 text: cm.getColumnHeader(i),
53396                 checked: !cm.isHidden(i),
53397                 hideOnClick:false
53398             }));
53399         }
53400     },
53401
53402     handleHdCtx : function(g, index, e){
53403         e.stopEvent();
53404         var hd = this.getHeaderCell(index);
53405         this.hdCtxIndex = index;
53406         var ms = this.hmenu.items, cm = this.cm;
53407         ms.get("asc").setDisabled(!cm.isSortable(index));
53408         ms.get("desc").setDisabled(!cm.isSortable(index));
53409         if(this.grid.enableColLock !== false){
53410             ms.get("lock").setDisabled(cm.isLocked(index));
53411             ms.get("unlock").setDisabled(!cm.isLocked(index));
53412         }
53413         this.hmenu.show(hd, "tl-bl");
53414     },
53415
53416     handleHdOver : function(e){
53417         var hd = this.findHeaderCell(e.getTarget());
53418         if(hd && !this.headersDisabled){
53419             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53420                this.fly(hd).addClass("x-grid-hd-over");
53421             }
53422         }
53423     },
53424
53425     handleHdOut : function(e){
53426         var hd = this.findHeaderCell(e.getTarget());
53427         if(hd){
53428             this.fly(hd).removeClass("x-grid-hd-over");
53429         }
53430     },
53431
53432     handleSplitDblClick : function(e, t){
53433         var i = this.getCellIndex(t);
53434         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53435             this.autoSizeColumn(i, true);
53436             this.layout();
53437         }
53438     },
53439
53440     render : function(){
53441
53442         var cm = this.cm;
53443         var colCount = cm.getColumnCount();
53444
53445         if(this.grid.monitorWindowResize === true){
53446             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53447         }
53448         var header = this.renderHeaders();
53449         var body = this.templates.body.apply({rows:""});
53450         var html = this.templates.master.apply({
53451             lockedBody: body,
53452             body: body,
53453             lockedHeader: header[0],
53454             header: header[1]
53455         });
53456
53457         //this.updateColumns();
53458
53459         this.grid.getGridEl().dom.innerHTML = html;
53460
53461         this.initElements();
53462         
53463         // a kludge to fix the random scolling effect in webkit
53464         this.el.on("scroll", function() {
53465             this.el.dom.scrollTop=0; // hopefully not recursive..
53466         },this);
53467
53468         this.scroller.on("scroll", this.handleScroll, this);
53469         this.lockedBody.on("mousewheel", this.handleWheel, this);
53470         this.mainBody.on("mousewheel", this.handleWheel, this);
53471
53472         this.mainHd.on("mouseover", this.handleHdOver, this);
53473         this.mainHd.on("mouseout", this.handleHdOut, this);
53474         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53475                 {delegate: "."+this.splitClass});
53476
53477         this.lockedHd.on("mouseover", this.handleHdOver, this);
53478         this.lockedHd.on("mouseout", this.handleHdOut, this);
53479         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53480                 {delegate: "."+this.splitClass});
53481
53482         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53483             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53484         }
53485
53486         this.updateSplitters();
53487
53488         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53489             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53490             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53491         }
53492
53493         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53494             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53495             this.hmenu.add(
53496                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53497                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53498             );
53499             if(this.grid.enableColLock !== false){
53500                 this.hmenu.add('-',
53501                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53502                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53503                 );
53504             }
53505             if(this.grid.enableColumnHide !== false){
53506
53507                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53508                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53509                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53510
53511                 this.hmenu.add('-',
53512                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53513                 );
53514             }
53515             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53516
53517             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53518         }
53519
53520         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53521             this.dd = new Roo.grid.GridDragZone(this.grid, {
53522                 ddGroup : this.grid.ddGroup || 'GridDD'
53523             });
53524             
53525         }
53526
53527         /*
53528         for(var i = 0; i < colCount; i++){
53529             if(cm.isHidden(i)){
53530                 this.hideColumn(i);
53531             }
53532             if(cm.config[i].align){
53533                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53534                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53535             }
53536         }*/
53537         
53538         this.updateHeaderSortState();
53539
53540         this.beforeInitialResize();
53541         this.layout(true);
53542
53543         // two part rendering gives faster view to the user
53544         this.renderPhase2.defer(1, this);
53545     },
53546
53547     renderPhase2 : function(){
53548         // render the rows now
53549         this.refresh();
53550         if(this.grid.autoSizeColumns){
53551             this.autoSizeColumns();
53552         }
53553     },
53554
53555     beforeInitialResize : function(){
53556
53557     },
53558
53559     onColumnSplitterMoved : function(i, w){
53560         this.userResized = true;
53561         var cm = this.grid.colModel;
53562         cm.setColumnWidth(i, w, true);
53563         var cid = cm.getColumnId(i);
53564         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53565         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53566         this.updateSplitters();
53567         this.layout();
53568         this.grid.fireEvent("columnresize", i, w);
53569     },
53570
53571     syncRowHeights : function(startIndex, endIndex){
53572         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53573             startIndex = startIndex || 0;
53574             var mrows = this.getBodyTable().rows;
53575             var lrows = this.getLockedTable().rows;
53576             var len = mrows.length-1;
53577             endIndex = Math.min(endIndex || len, len);
53578             for(var i = startIndex; i <= endIndex; i++){
53579                 var m = mrows[i], l = lrows[i];
53580                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53581                 m.style.height = l.style.height = h + "px";
53582             }
53583         }
53584     },
53585
53586     layout : function(initialRender, is2ndPass){
53587         var g = this.grid;
53588         var auto = g.autoHeight;
53589         var scrollOffset = 16;
53590         var c = g.getGridEl(), cm = this.cm,
53591                 expandCol = g.autoExpandColumn,
53592                 gv = this;
53593         //c.beginMeasure();
53594
53595         if(!c.dom.offsetWidth){ // display:none?
53596             if(initialRender){
53597                 this.lockedWrap.show();
53598                 this.mainWrap.show();
53599             }
53600             return;
53601         }
53602
53603         var hasLock = this.cm.isLocked(0);
53604
53605         var tbh = this.headerPanel.getHeight();
53606         var bbh = this.footerPanel.getHeight();
53607
53608         if(auto){
53609             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53610             var newHeight = ch + c.getBorderWidth("tb");
53611             if(g.maxHeight){
53612                 newHeight = Math.min(g.maxHeight, newHeight);
53613             }
53614             c.setHeight(newHeight);
53615         }
53616
53617         if(g.autoWidth){
53618             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53619         }
53620
53621         var s = this.scroller;
53622
53623         var csize = c.getSize(true);
53624
53625         this.el.setSize(csize.width, csize.height);
53626
53627         this.headerPanel.setWidth(csize.width);
53628         this.footerPanel.setWidth(csize.width);
53629
53630         var hdHeight = this.mainHd.getHeight();
53631         var vw = csize.width;
53632         var vh = csize.height - (tbh + bbh);
53633
53634         s.setSize(vw, vh);
53635
53636         var bt = this.getBodyTable();
53637         var ltWidth = hasLock ?
53638                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
53639
53640         var scrollHeight = bt.offsetHeight;
53641         var scrollWidth = ltWidth + bt.offsetWidth;
53642         var vscroll = false, hscroll = false;
53643
53644         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
53645
53646         var lw = this.lockedWrap, mw = this.mainWrap;
53647         var lb = this.lockedBody, mb = this.mainBody;
53648
53649         setTimeout(function(){
53650             var t = s.dom.offsetTop;
53651             var w = s.dom.clientWidth,
53652                 h = s.dom.clientHeight;
53653
53654             lw.setTop(t);
53655             lw.setSize(ltWidth, h);
53656
53657             mw.setLeftTop(ltWidth, t);
53658             mw.setSize(w-ltWidth, h);
53659
53660             lb.setHeight(h-hdHeight);
53661             mb.setHeight(h-hdHeight);
53662
53663             if(is2ndPass !== true && !gv.userResized && expandCol){
53664                 // high speed resize without full column calculation
53665                 
53666                 var ci = cm.getIndexById(expandCol);
53667                 if (ci < 0) {
53668                     ci = cm.findColumnIndex(expandCol);
53669                 }
53670                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53671                 var expandId = cm.getColumnId(ci);
53672                 var  tw = cm.getTotalWidth(false);
53673                 var currentWidth = cm.getColumnWidth(ci);
53674                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53675                 if(currentWidth != cw){
53676                     cm.setColumnWidth(ci, cw, true);
53677                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53678                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53679                     gv.updateSplitters();
53680                     gv.layout(false, true);
53681                 }
53682             }
53683
53684             if(initialRender){
53685                 lw.show();
53686                 mw.show();
53687             }
53688             //c.endMeasure();
53689         }, 10);
53690     },
53691
53692     onWindowResize : function(){
53693         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53694             return;
53695         }
53696         this.layout();
53697     },
53698
53699     appendFooter : function(parentEl){
53700         return null;
53701     },
53702
53703     sortAscText : "Sort Ascending",
53704     sortDescText : "Sort Descending",
53705     lockText : "Lock Column",
53706     unlockText : "Unlock Column",
53707     columnsText : "Columns"
53708 });
53709
53710
53711 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53712     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53713     this.proxy.el.addClass('x-grid3-col-dd');
53714 };
53715
53716 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53717     handleMouseDown : function(e){
53718
53719     },
53720
53721     callHandleMouseDown : function(e){
53722         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53723     }
53724 });
53725 /*
53726  * Based on:
53727  * Ext JS Library 1.1.1
53728  * Copyright(c) 2006-2007, Ext JS, LLC.
53729  *
53730  * Originally Released Under LGPL - original licence link has changed is not relivant.
53731  *
53732  * Fork - LGPL
53733  * <script type="text/javascript">
53734  */
53735  
53736 // private
53737 // This is a support class used internally by the Grid components
53738 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53739     this.grid = grid;
53740     this.view = grid.getView();
53741     this.proxy = this.view.resizeProxy;
53742     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53743         "gridSplitters" + this.grid.getGridEl().id, {
53744         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53745     });
53746     this.setHandleElId(Roo.id(hd));
53747     this.setOuterHandleElId(Roo.id(hd2));
53748     this.scroll = false;
53749 };
53750 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53751     fly: Roo.Element.fly,
53752
53753     b4StartDrag : function(x, y){
53754         this.view.headersDisabled = true;
53755         this.proxy.setHeight(this.view.mainWrap.getHeight());
53756         var w = this.cm.getColumnWidth(this.cellIndex);
53757         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53758         this.resetConstraints();
53759         this.setXConstraint(minw, 1000);
53760         this.setYConstraint(0, 0);
53761         this.minX = x - minw;
53762         this.maxX = x + 1000;
53763         this.startPos = x;
53764         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53765     },
53766
53767
53768     handleMouseDown : function(e){
53769         ev = Roo.EventObject.setEvent(e);
53770         var t = this.fly(ev.getTarget());
53771         if(t.hasClass("x-grid-split")){
53772             this.cellIndex = this.view.getCellIndex(t.dom);
53773             this.split = t.dom;
53774             this.cm = this.grid.colModel;
53775             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53776                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53777             }
53778         }
53779     },
53780
53781     endDrag : function(e){
53782         this.view.headersDisabled = false;
53783         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53784         var diff = endX - this.startPos;
53785         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53786     },
53787
53788     autoOffset : function(){
53789         this.setDelta(0,0);
53790     }
53791 });/*
53792  * Based on:
53793  * Ext JS Library 1.1.1
53794  * Copyright(c) 2006-2007, Ext JS, LLC.
53795  *
53796  * Originally Released Under LGPL - original licence link has changed is not relivant.
53797  *
53798  * Fork - LGPL
53799  * <script type="text/javascript">
53800  */
53801  
53802 // private
53803 // This is a support class used internally by the Grid components
53804 Roo.grid.GridDragZone = function(grid, config){
53805     this.view = grid.getView();
53806     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53807     if(this.view.lockedBody){
53808         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53809         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53810     }
53811     this.scroll = false;
53812     this.grid = grid;
53813     this.ddel = document.createElement('div');
53814     this.ddel.className = 'x-grid-dd-wrap';
53815 };
53816
53817 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53818     ddGroup : "GridDD",
53819
53820     getDragData : function(e){
53821         var t = Roo.lib.Event.getTarget(e);
53822         var rowIndex = this.view.findRowIndex(t);
53823         var sm = this.grid.selModel;
53824             
53825         //Roo.log(rowIndex);
53826         
53827         if (sm.getSelectedCell) {
53828             // cell selection..
53829             if (!sm.getSelectedCell()) {
53830                 return false;
53831             }
53832             if (rowIndex != sm.getSelectedCell()[0]) {
53833                 return false;
53834             }
53835         
53836         }
53837         
53838         if(rowIndex !== false){
53839             
53840             // if editorgrid.. 
53841             
53842             
53843             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53844                
53845             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53846               //  
53847             //}
53848             if (e.hasModifier()){
53849                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53850             }
53851             
53852             Roo.log("getDragData");
53853             
53854             return {
53855                 grid: this.grid,
53856                 ddel: this.ddel,
53857                 rowIndex: rowIndex,
53858                 selections:sm.getSelections ? sm.getSelections() : (
53859                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53860                 )
53861             };
53862         }
53863         return false;
53864     },
53865
53866     onInitDrag : function(e){
53867         var data = this.dragData;
53868         this.ddel.innerHTML = this.grid.getDragDropText();
53869         this.proxy.update(this.ddel);
53870         // fire start drag?
53871     },
53872
53873     afterRepair : function(){
53874         this.dragging = false;
53875     },
53876
53877     getRepairXY : function(e, data){
53878         return false;
53879     },
53880
53881     onEndDrag : function(data, e){
53882         // fire end drag?
53883     },
53884
53885     onValidDrop : function(dd, e, id){
53886         // fire drag drop?
53887         this.hideProxy();
53888     },
53889
53890     beforeInvalidDrop : function(e, id){
53891
53892     }
53893 });/*
53894  * Based on:
53895  * Ext JS Library 1.1.1
53896  * Copyright(c) 2006-2007, Ext JS, LLC.
53897  *
53898  * Originally Released Under LGPL - original licence link has changed is not relivant.
53899  *
53900  * Fork - LGPL
53901  * <script type="text/javascript">
53902  */
53903  
53904
53905 /**
53906  * @class Roo.grid.ColumnModel
53907  * @extends Roo.util.Observable
53908  * This is the default implementation of a ColumnModel used by the Grid. It defines
53909  * the columns in the grid.
53910  * <br>Usage:<br>
53911  <pre><code>
53912  var colModel = new Roo.grid.ColumnModel([
53913         {header: "Ticker", width: 60, sortable: true, locked: true},
53914         {header: "Company Name", width: 150, sortable: true},
53915         {header: "Market Cap.", width: 100, sortable: true},
53916         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53917         {header: "Employees", width: 100, sortable: true, resizable: false}
53918  ]);
53919  </code></pre>
53920  * <p>
53921  
53922  * The config options listed for this class are options which may appear in each
53923  * individual column definition.
53924  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53925  * @constructor
53926  * @param {Object} config An Array of column config objects. See this class's
53927  * config objects for details.
53928 */
53929 Roo.grid.ColumnModel = function(config){
53930         /**
53931      * The config passed into the constructor
53932      */
53933     this.config = config;
53934     this.lookup = {};
53935
53936     // if no id, create one
53937     // if the column does not have a dataIndex mapping,
53938     // map it to the order it is in the config
53939     for(var i = 0, len = config.length; i < len; i++){
53940         var c = config[i];
53941         if(typeof c.dataIndex == "undefined"){
53942             c.dataIndex = i;
53943         }
53944         if(typeof c.renderer == "string"){
53945             c.renderer = Roo.util.Format[c.renderer];
53946         }
53947         if(typeof c.id == "undefined"){
53948             c.id = Roo.id();
53949         }
53950         if(c.editor && c.editor.xtype){
53951             c.editor  = Roo.factory(c.editor, Roo.grid);
53952         }
53953         if(c.editor && c.editor.isFormField){
53954             c.editor = new Roo.grid.GridEditor(c.editor);
53955         }
53956         this.lookup[c.id] = c;
53957     }
53958
53959     /**
53960      * The width of columns which have no width specified (defaults to 100)
53961      * @type Number
53962      */
53963     this.defaultWidth = 100;
53964
53965     /**
53966      * Default sortable of columns which have no sortable specified (defaults to false)
53967      * @type Boolean
53968      */
53969     this.defaultSortable = false;
53970
53971     this.addEvents({
53972         /**
53973              * @event widthchange
53974              * Fires when the width of a column changes.
53975              * @param {ColumnModel} this
53976              * @param {Number} columnIndex The column index
53977              * @param {Number} newWidth The new width
53978              */
53979             "widthchange": true,
53980         /**
53981              * @event headerchange
53982              * Fires when the text of a header changes.
53983              * @param {ColumnModel} this
53984              * @param {Number} columnIndex The column index
53985              * @param {Number} newText The new header text
53986              */
53987             "headerchange": true,
53988         /**
53989              * @event hiddenchange
53990              * Fires when a column is hidden or "unhidden".
53991              * @param {ColumnModel} this
53992              * @param {Number} columnIndex The column index
53993              * @param {Boolean} hidden true if hidden, false otherwise
53994              */
53995             "hiddenchange": true,
53996             /**
53997          * @event columnmoved
53998          * Fires when a column is moved.
53999          * @param {ColumnModel} this
54000          * @param {Number} oldIndex
54001          * @param {Number} newIndex
54002          */
54003         "columnmoved" : true,
54004         /**
54005          * @event columlockchange
54006          * Fires when a column's locked state is changed
54007          * @param {ColumnModel} this
54008          * @param {Number} colIndex
54009          * @param {Boolean} locked true if locked
54010          */
54011         "columnlockchange" : true
54012     });
54013     Roo.grid.ColumnModel.superclass.constructor.call(this);
54014 };
54015 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54016     /**
54017      * @cfg {String} header The header text to display in the Grid view.
54018      */
54019     /**
54020      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54021      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54022      * specified, the column's index is used as an index into the Record's data Array.
54023      */
54024     /**
54025      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54026      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54027      */
54028     /**
54029      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54030      * Defaults to the value of the {@link #defaultSortable} property.
54031      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54032      */
54033     /**
54034      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54035      */
54036     /**
54037      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54038      */
54039     /**
54040      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54041      */
54042     /**
54043      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54044      */
54045     /**
54046      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54047      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54048      * default renderer uses the raw data value.
54049      */
54050        /**
54051      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54052      */
54053     /**
54054      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54055      */
54056
54057     /**
54058      * Returns the id of the column at the specified index.
54059      * @param {Number} index The column index
54060      * @return {String} the id
54061      */
54062     getColumnId : function(index){
54063         return this.config[index].id;
54064     },
54065
54066     /**
54067      * Returns the column for a specified id.
54068      * @param {String} id The column id
54069      * @return {Object} the column
54070      */
54071     getColumnById : function(id){
54072         return this.lookup[id];
54073     },
54074
54075     
54076     /**
54077      * Returns the column for a specified dataIndex.
54078      * @param {String} dataIndex The column dataIndex
54079      * @return {Object|Boolean} the column or false if not found
54080      */
54081     getColumnByDataIndex: function(dataIndex){
54082         var index = this.findColumnIndex(dataIndex);
54083         return index > -1 ? this.config[index] : false;
54084     },
54085     
54086     /**
54087      * Returns the index for a specified column id.
54088      * @param {String} id The column id
54089      * @return {Number} the index, or -1 if not found
54090      */
54091     getIndexById : function(id){
54092         for(var i = 0, len = this.config.length; i < len; i++){
54093             if(this.config[i].id == id){
54094                 return i;
54095             }
54096         }
54097         return -1;
54098     },
54099     
54100     /**
54101      * Returns the index for a specified column dataIndex.
54102      * @param {String} dataIndex The column dataIndex
54103      * @return {Number} the index, or -1 if not found
54104      */
54105     
54106     findColumnIndex : function(dataIndex){
54107         for(var i = 0, len = this.config.length; i < len; i++){
54108             if(this.config[i].dataIndex == dataIndex){
54109                 return i;
54110             }
54111         }
54112         return -1;
54113     },
54114     
54115     
54116     moveColumn : function(oldIndex, newIndex){
54117         var c = this.config[oldIndex];
54118         this.config.splice(oldIndex, 1);
54119         this.config.splice(newIndex, 0, c);
54120         this.dataMap = null;
54121         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54122     },
54123
54124     isLocked : function(colIndex){
54125         return this.config[colIndex].locked === true;
54126     },
54127
54128     setLocked : function(colIndex, value, suppressEvent){
54129         if(this.isLocked(colIndex) == value){
54130             return;
54131         }
54132         this.config[colIndex].locked = value;
54133         if(!suppressEvent){
54134             this.fireEvent("columnlockchange", this, colIndex, value);
54135         }
54136     },
54137
54138     getTotalLockedWidth : function(){
54139         var totalWidth = 0;
54140         for(var i = 0; i < this.config.length; i++){
54141             if(this.isLocked(i) && !this.isHidden(i)){
54142                 this.totalWidth += this.getColumnWidth(i);
54143             }
54144         }
54145         return totalWidth;
54146     },
54147
54148     getLockedCount : function(){
54149         for(var i = 0, len = this.config.length; i < len; i++){
54150             if(!this.isLocked(i)){
54151                 return i;
54152             }
54153         }
54154     },
54155
54156     /**
54157      * Returns the number of columns.
54158      * @return {Number}
54159      */
54160     getColumnCount : function(visibleOnly){
54161         if(visibleOnly === true){
54162             var c = 0;
54163             for(var i = 0, len = this.config.length; i < len; i++){
54164                 if(!this.isHidden(i)){
54165                     c++;
54166                 }
54167             }
54168             return c;
54169         }
54170         return this.config.length;
54171     },
54172
54173     /**
54174      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54175      * @param {Function} fn
54176      * @param {Object} scope (optional)
54177      * @return {Array} result
54178      */
54179     getColumnsBy : function(fn, scope){
54180         var r = [];
54181         for(var i = 0, len = this.config.length; i < len; i++){
54182             var c = this.config[i];
54183             if(fn.call(scope||this, c, i) === true){
54184                 r[r.length] = c;
54185             }
54186         }
54187         return r;
54188     },
54189
54190     /**
54191      * Returns true if the specified column is sortable.
54192      * @param {Number} col The column index
54193      * @return {Boolean}
54194      */
54195     isSortable : function(col){
54196         if(typeof this.config[col].sortable == "undefined"){
54197             return this.defaultSortable;
54198         }
54199         return this.config[col].sortable;
54200     },
54201
54202     /**
54203      * Returns the rendering (formatting) function defined for the column.
54204      * @param {Number} col The column index.
54205      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54206      */
54207     getRenderer : function(col){
54208         if(!this.config[col].renderer){
54209             return Roo.grid.ColumnModel.defaultRenderer;
54210         }
54211         return this.config[col].renderer;
54212     },
54213
54214     /**
54215      * Sets the rendering (formatting) function for a column.
54216      * @param {Number} col The column index
54217      * @param {Function} fn The function to use to process the cell's raw data
54218      * to return HTML markup for the grid view. The render function is called with
54219      * the following parameters:<ul>
54220      * <li>Data value.</li>
54221      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54222      * <li>css A CSS style string to apply to the table cell.</li>
54223      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54224      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54225      * <li>Row index</li>
54226      * <li>Column index</li>
54227      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54228      */
54229     setRenderer : function(col, fn){
54230         this.config[col].renderer = fn;
54231     },
54232
54233     /**
54234      * Returns the width for the specified column.
54235      * @param {Number} col The column index
54236      * @return {Number}
54237      */
54238     getColumnWidth : function(col){
54239         return this.config[col].width * 1 || this.defaultWidth;
54240     },
54241
54242     /**
54243      * Sets the width for a column.
54244      * @param {Number} col The column index
54245      * @param {Number} width The new width
54246      */
54247     setColumnWidth : function(col, width, suppressEvent){
54248         this.config[col].width = width;
54249         this.totalWidth = null;
54250         if(!suppressEvent){
54251              this.fireEvent("widthchange", this, col, width);
54252         }
54253     },
54254
54255     /**
54256      * Returns the total width of all columns.
54257      * @param {Boolean} includeHidden True to include hidden column widths
54258      * @return {Number}
54259      */
54260     getTotalWidth : function(includeHidden){
54261         if(!this.totalWidth){
54262             this.totalWidth = 0;
54263             for(var i = 0, len = this.config.length; i < len; i++){
54264                 if(includeHidden || !this.isHidden(i)){
54265                     this.totalWidth += this.getColumnWidth(i);
54266                 }
54267             }
54268         }
54269         return this.totalWidth;
54270     },
54271
54272     /**
54273      * Returns the header for the specified column.
54274      * @param {Number} col The column index
54275      * @return {String}
54276      */
54277     getColumnHeader : function(col){
54278         return this.config[col].header;
54279     },
54280
54281     /**
54282      * Sets the header for a column.
54283      * @param {Number} col The column index
54284      * @param {String} header The new header
54285      */
54286     setColumnHeader : function(col, header){
54287         this.config[col].header = header;
54288         this.fireEvent("headerchange", this, col, header);
54289     },
54290
54291     /**
54292      * Returns the tooltip for the specified column.
54293      * @param {Number} col The column index
54294      * @return {String}
54295      */
54296     getColumnTooltip : function(col){
54297             return this.config[col].tooltip;
54298     },
54299     /**
54300      * Sets the tooltip for a column.
54301      * @param {Number} col The column index
54302      * @param {String} tooltip The new tooltip
54303      */
54304     setColumnTooltip : function(col, tooltip){
54305             this.config[col].tooltip = tooltip;
54306     },
54307
54308     /**
54309      * Returns the dataIndex for the specified column.
54310      * @param {Number} col The column index
54311      * @return {Number}
54312      */
54313     getDataIndex : function(col){
54314         return this.config[col].dataIndex;
54315     },
54316
54317     /**
54318      * Sets the dataIndex for a column.
54319      * @param {Number} col The column index
54320      * @param {Number} dataIndex The new dataIndex
54321      */
54322     setDataIndex : function(col, dataIndex){
54323         this.config[col].dataIndex = dataIndex;
54324     },
54325
54326     
54327     
54328     /**
54329      * Returns true if the cell is editable.
54330      * @param {Number} colIndex The column index
54331      * @param {Number} rowIndex The row index
54332      * @return {Boolean}
54333      */
54334     isCellEditable : function(colIndex, rowIndex){
54335         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54336     },
54337
54338     /**
54339      * Returns the editor defined for the cell/column.
54340      * return false or null to disable editing.
54341      * @param {Number} colIndex The column index
54342      * @param {Number} rowIndex The row index
54343      * @return {Object}
54344      */
54345     getCellEditor : function(colIndex, rowIndex){
54346         return this.config[colIndex].editor;
54347     },
54348
54349     /**
54350      * Sets if a column is editable.
54351      * @param {Number} col The column index
54352      * @param {Boolean} editable True if the column is editable
54353      */
54354     setEditable : function(col, editable){
54355         this.config[col].editable = editable;
54356     },
54357
54358
54359     /**
54360      * Returns true if the column is hidden.
54361      * @param {Number} colIndex The column index
54362      * @return {Boolean}
54363      */
54364     isHidden : function(colIndex){
54365         return this.config[colIndex].hidden;
54366     },
54367
54368
54369     /**
54370      * Returns true if the column width cannot be changed
54371      */
54372     isFixed : function(colIndex){
54373         return this.config[colIndex].fixed;
54374     },
54375
54376     /**
54377      * Returns true if the column can be resized
54378      * @return {Boolean}
54379      */
54380     isResizable : function(colIndex){
54381         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54382     },
54383     /**
54384      * Sets if a column is hidden.
54385      * @param {Number} colIndex The column index
54386      * @param {Boolean} hidden True if the column is hidden
54387      */
54388     setHidden : function(colIndex, hidden){
54389         this.config[colIndex].hidden = hidden;
54390         this.totalWidth = null;
54391         this.fireEvent("hiddenchange", this, colIndex, hidden);
54392     },
54393
54394     /**
54395      * Sets the editor for a column.
54396      * @param {Number} col The column index
54397      * @param {Object} editor The editor object
54398      */
54399     setEditor : function(col, editor){
54400         this.config[col].editor = editor;
54401     }
54402 });
54403
54404 Roo.grid.ColumnModel.defaultRenderer = function(value){
54405         if(typeof value == "string" && value.length < 1){
54406             return "&#160;";
54407         }
54408         return value;
54409 };
54410
54411 // Alias for backwards compatibility
54412 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54413 /*
54414  * Based on:
54415  * Ext JS Library 1.1.1
54416  * Copyright(c) 2006-2007, Ext JS, LLC.
54417  *
54418  * Originally Released Under LGPL - original licence link has changed is not relivant.
54419  *
54420  * Fork - LGPL
54421  * <script type="text/javascript">
54422  */
54423
54424 /**
54425  * @class Roo.grid.AbstractSelectionModel
54426  * @extends Roo.util.Observable
54427  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54428  * implemented by descendant classes.  This class should not be directly instantiated.
54429  * @constructor
54430  */
54431 Roo.grid.AbstractSelectionModel = function(){
54432     this.locked = false;
54433     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54434 };
54435
54436 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54437     /** @ignore Called by the grid automatically. Do not call directly. */
54438     init : function(grid){
54439         this.grid = grid;
54440         this.initEvents();
54441     },
54442
54443     /**
54444      * Locks the selections.
54445      */
54446     lock : function(){
54447         this.locked = true;
54448     },
54449
54450     /**
54451      * Unlocks the selections.
54452      */
54453     unlock : function(){
54454         this.locked = false;
54455     },
54456
54457     /**
54458      * Returns true if the selections are locked.
54459      * @return {Boolean}
54460      */
54461     isLocked : function(){
54462         return this.locked;
54463     }
54464 });/*
54465  * Based on:
54466  * Ext JS Library 1.1.1
54467  * Copyright(c) 2006-2007, Ext JS, LLC.
54468  *
54469  * Originally Released Under LGPL - original licence link has changed is not relivant.
54470  *
54471  * Fork - LGPL
54472  * <script type="text/javascript">
54473  */
54474 /**
54475  * @extends Roo.grid.AbstractSelectionModel
54476  * @class Roo.grid.RowSelectionModel
54477  * The default SelectionModel used by {@link Roo.grid.Grid}.
54478  * It supports multiple selections and keyboard selection/navigation. 
54479  * @constructor
54480  * @param {Object} config
54481  */
54482 Roo.grid.RowSelectionModel = function(config){
54483     Roo.apply(this, config);
54484     this.selections = new Roo.util.MixedCollection(false, function(o){
54485         return o.id;
54486     });
54487
54488     this.last = false;
54489     this.lastActive = false;
54490
54491     this.addEvents({
54492         /**
54493              * @event selectionchange
54494              * Fires when the selection changes
54495              * @param {SelectionModel} this
54496              */
54497             "selectionchange" : true,
54498         /**
54499              * @event afterselectionchange
54500              * Fires after the selection changes (eg. by key press or clicking)
54501              * @param {SelectionModel} this
54502              */
54503             "afterselectionchange" : true,
54504         /**
54505              * @event beforerowselect
54506              * Fires when a row is selected being selected, return false to cancel.
54507              * @param {SelectionModel} this
54508              * @param {Number} rowIndex The selected index
54509              * @param {Boolean} keepExisting False if other selections will be cleared
54510              */
54511             "beforerowselect" : true,
54512         /**
54513              * @event rowselect
54514              * Fires when a row is selected.
54515              * @param {SelectionModel} this
54516              * @param {Number} rowIndex The selected index
54517              * @param {Roo.data.Record} r The record
54518              */
54519             "rowselect" : true,
54520         /**
54521              * @event rowdeselect
54522              * Fires when a row is deselected.
54523              * @param {SelectionModel} this
54524              * @param {Number} rowIndex The selected index
54525              */
54526         "rowdeselect" : true
54527     });
54528     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54529     this.locked = false;
54530 };
54531
54532 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54533     /**
54534      * @cfg {Boolean} singleSelect
54535      * True to allow selection of only one row at a time (defaults to false)
54536      */
54537     singleSelect : false,
54538
54539     // private
54540     initEvents : function(){
54541
54542         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54543             this.grid.on("mousedown", this.handleMouseDown, this);
54544         }else{ // allow click to work like normal
54545             this.grid.on("rowclick", this.handleDragableRowClick, this);
54546         }
54547
54548         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54549             "up" : function(e){
54550                 if(!e.shiftKey){
54551                     this.selectPrevious(e.shiftKey);
54552                 }else if(this.last !== false && this.lastActive !== false){
54553                     var last = this.last;
54554                     this.selectRange(this.last,  this.lastActive-1);
54555                     this.grid.getView().focusRow(this.lastActive);
54556                     if(last !== false){
54557                         this.last = last;
54558                     }
54559                 }else{
54560                     this.selectFirstRow();
54561                 }
54562                 this.fireEvent("afterselectionchange", this);
54563             },
54564             "down" : function(e){
54565                 if(!e.shiftKey){
54566                     this.selectNext(e.shiftKey);
54567                 }else if(this.last !== false && this.lastActive !== false){
54568                     var last = this.last;
54569                     this.selectRange(this.last,  this.lastActive+1);
54570                     this.grid.getView().focusRow(this.lastActive);
54571                     if(last !== false){
54572                         this.last = last;
54573                     }
54574                 }else{
54575                     this.selectFirstRow();
54576                 }
54577                 this.fireEvent("afterselectionchange", this);
54578             },
54579             scope: this
54580         });
54581
54582         var view = this.grid.view;
54583         view.on("refresh", this.onRefresh, this);
54584         view.on("rowupdated", this.onRowUpdated, this);
54585         view.on("rowremoved", this.onRemove, this);
54586     },
54587
54588     // private
54589     onRefresh : function(){
54590         var ds = this.grid.dataSource, i, v = this.grid.view;
54591         var s = this.selections;
54592         s.each(function(r){
54593             if((i = ds.indexOfId(r.id)) != -1){
54594                 v.onRowSelect(i);
54595             }else{
54596                 s.remove(r);
54597             }
54598         });
54599     },
54600
54601     // private
54602     onRemove : function(v, index, r){
54603         this.selections.remove(r);
54604     },
54605
54606     // private
54607     onRowUpdated : function(v, index, r){
54608         if(this.isSelected(r)){
54609             v.onRowSelect(index);
54610         }
54611     },
54612
54613     /**
54614      * Select records.
54615      * @param {Array} records The records to select
54616      * @param {Boolean} keepExisting (optional) True to keep existing selections
54617      */
54618     selectRecords : function(records, keepExisting){
54619         if(!keepExisting){
54620             this.clearSelections();
54621         }
54622         var ds = this.grid.dataSource;
54623         for(var i = 0, len = records.length; i < len; i++){
54624             this.selectRow(ds.indexOf(records[i]), true);
54625         }
54626     },
54627
54628     /**
54629      * Gets the number of selected rows.
54630      * @return {Number}
54631      */
54632     getCount : function(){
54633         return this.selections.length;
54634     },
54635
54636     /**
54637      * Selects the first row in the grid.
54638      */
54639     selectFirstRow : function(){
54640         this.selectRow(0);
54641     },
54642
54643     /**
54644      * Select the last row.
54645      * @param {Boolean} keepExisting (optional) True to keep existing selections
54646      */
54647     selectLastRow : function(keepExisting){
54648         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
54649     },
54650
54651     /**
54652      * Selects the row immediately following the last selected row.
54653      * @param {Boolean} keepExisting (optional) True to keep existing selections
54654      */
54655     selectNext : function(keepExisting){
54656         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54657             this.selectRow(this.last+1, keepExisting);
54658             this.grid.getView().focusRow(this.last);
54659         }
54660     },
54661
54662     /**
54663      * Selects the row that precedes the last selected row.
54664      * @param {Boolean} keepExisting (optional) True to keep existing selections
54665      */
54666     selectPrevious : function(keepExisting){
54667         if(this.last){
54668             this.selectRow(this.last-1, keepExisting);
54669             this.grid.getView().focusRow(this.last);
54670         }
54671     },
54672
54673     /**
54674      * Returns the selected records
54675      * @return {Array} Array of selected records
54676      */
54677     getSelections : function(){
54678         return [].concat(this.selections.items);
54679     },
54680
54681     /**
54682      * Returns the first selected record.
54683      * @return {Record}
54684      */
54685     getSelected : function(){
54686         return this.selections.itemAt(0);
54687     },
54688
54689
54690     /**
54691      * Clears all selections.
54692      */
54693     clearSelections : function(fast){
54694         if(this.locked) return;
54695         if(fast !== true){
54696             var ds = this.grid.dataSource;
54697             var s = this.selections;
54698             s.each(function(r){
54699                 this.deselectRow(ds.indexOfId(r.id));
54700             }, this);
54701             s.clear();
54702         }else{
54703             this.selections.clear();
54704         }
54705         this.last = false;
54706     },
54707
54708
54709     /**
54710      * Selects all rows.
54711      */
54712     selectAll : function(){
54713         if(this.locked) return;
54714         this.selections.clear();
54715         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54716             this.selectRow(i, true);
54717         }
54718     },
54719
54720     /**
54721      * Returns True if there is a selection.
54722      * @return {Boolean}
54723      */
54724     hasSelection : function(){
54725         return this.selections.length > 0;
54726     },
54727
54728     /**
54729      * Returns True if the specified row is selected.
54730      * @param {Number/Record} record The record or index of the record to check
54731      * @return {Boolean}
54732      */
54733     isSelected : function(index){
54734         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54735         return (r && this.selections.key(r.id) ? true : false);
54736     },
54737
54738     /**
54739      * Returns True if the specified record id is selected.
54740      * @param {String} id The id of record to check
54741      * @return {Boolean}
54742      */
54743     isIdSelected : function(id){
54744         return (this.selections.key(id) ? true : false);
54745     },
54746
54747     // private
54748     handleMouseDown : function(e, t){
54749         var view = this.grid.getView(), rowIndex;
54750         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54751             return;
54752         };
54753         if(e.shiftKey && this.last !== false){
54754             var last = this.last;
54755             this.selectRange(last, rowIndex, e.ctrlKey);
54756             this.last = last; // reset the last
54757             view.focusRow(rowIndex);
54758         }else{
54759             var isSelected = this.isSelected(rowIndex);
54760             if(e.button !== 0 && isSelected){
54761                 view.focusRow(rowIndex);
54762             }else if(e.ctrlKey && isSelected){
54763                 this.deselectRow(rowIndex);
54764             }else if(!isSelected){
54765                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54766                 view.focusRow(rowIndex);
54767             }
54768         }
54769         this.fireEvent("afterselectionchange", this);
54770     },
54771     // private
54772     handleDragableRowClick :  function(grid, rowIndex, e) 
54773     {
54774         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54775             this.selectRow(rowIndex, false);
54776             grid.view.focusRow(rowIndex);
54777              this.fireEvent("afterselectionchange", this);
54778         }
54779     },
54780     
54781     /**
54782      * Selects multiple rows.
54783      * @param {Array} rows Array of the indexes of the row to select
54784      * @param {Boolean} keepExisting (optional) True to keep existing selections
54785      */
54786     selectRows : function(rows, keepExisting){
54787         if(!keepExisting){
54788             this.clearSelections();
54789         }
54790         for(var i = 0, len = rows.length; i < len; i++){
54791             this.selectRow(rows[i], true);
54792         }
54793     },
54794
54795     /**
54796      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54797      * @param {Number} startRow The index of the first row in the range
54798      * @param {Number} endRow The index of the last row in the range
54799      * @param {Boolean} keepExisting (optional) True to retain existing selections
54800      */
54801     selectRange : function(startRow, endRow, keepExisting){
54802         if(this.locked) return;
54803         if(!keepExisting){
54804             this.clearSelections();
54805         }
54806         if(startRow <= endRow){
54807             for(var i = startRow; i <= endRow; i++){
54808                 this.selectRow(i, true);
54809             }
54810         }else{
54811             for(var i = startRow; i >= endRow; i--){
54812                 this.selectRow(i, true);
54813             }
54814         }
54815     },
54816
54817     /**
54818      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54819      * @param {Number} startRow The index of the first row in the range
54820      * @param {Number} endRow The index of the last row in the range
54821      */
54822     deselectRange : function(startRow, endRow, preventViewNotify){
54823         if(this.locked) return;
54824         for(var i = startRow; i <= endRow; i++){
54825             this.deselectRow(i, preventViewNotify);
54826         }
54827     },
54828
54829     /**
54830      * Selects a row.
54831      * @param {Number} row The index of the row to select
54832      * @param {Boolean} keepExisting (optional) True to keep existing selections
54833      */
54834     selectRow : function(index, keepExisting, preventViewNotify){
54835         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54836         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54837             if(!keepExisting || this.singleSelect){
54838                 this.clearSelections();
54839             }
54840             var r = this.grid.dataSource.getAt(index);
54841             this.selections.add(r);
54842             this.last = this.lastActive = index;
54843             if(!preventViewNotify){
54844                 this.grid.getView().onRowSelect(index);
54845             }
54846             this.fireEvent("rowselect", this, index, r);
54847             this.fireEvent("selectionchange", this);
54848         }
54849     },
54850
54851     /**
54852      * Deselects a row.
54853      * @param {Number} row The index of the row to deselect
54854      */
54855     deselectRow : function(index, preventViewNotify){
54856         if(this.locked) return;
54857         if(this.last == index){
54858             this.last = false;
54859         }
54860         if(this.lastActive == index){
54861             this.lastActive = false;
54862         }
54863         var r = this.grid.dataSource.getAt(index);
54864         this.selections.remove(r);
54865         if(!preventViewNotify){
54866             this.grid.getView().onRowDeselect(index);
54867         }
54868         this.fireEvent("rowdeselect", this, index);
54869         this.fireEvent("selectionchange", this);
54870     },
54871
54872     // private
54873     restoreLast : function(){
54874         if(this._last){
54875             this.last = this._last;
54876         }
54877     },
54878
54879     // private
54880     acceptsNav : function(row, col, cm){
54881         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54882     },
54883
54884     // private
54885     onEditorKey : function(field, e){
54886         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54887         if(k == e.TAB){
54888             e.stopEvent();
54889             ed.completeEdit();
54890             if(e.shiftKey){
54891                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54892             }else{
54893                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54894             }
54895         }else if(k == e.ENTER && !e.ctrlKey){
54896             e.stopEvent();
54897             ed.completeEdit();
54898             if(e.shiftKey){
54899                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54900             }else{
54901                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54902             }
54903         }else if(k == e.ESC){
54904             ed.cancelEdit();
54905         }
54906         if(newCell){
54907             g.startEditing(newCell[0], newCell[1]);
54908         }
54909     }
54910 });/*
54911  * Based on:
54912  * Ext JS Library 1.1.1
54913  * Copyright(c) 2006-2007, Ext JS, LLC.
54914  *
54915  * Originally Released Under LGPL - original licence link has changed is not relivant.
54916  *
54917  * Fork - LGPL
54918  * <script type="text/javascript">
54919  */
54920 /**
54921  * @class Roo.grid.CellSelectionModel
54922  * @extends Roo.grid.AbstractSelectionModel
54923  * This class provides the basic implementation for cell selection in a grid.
54924  * @constructor
54925  * @param {Object} config The object containing the configuration of this model.
54926  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54927  */
54928 Roo.grid.CellSelectionModel = function(config){
54929     Roo.apply(this, config);
54930
54931     this.selection = null;
54932
54933     this.addEvents({
54934         /**
54935              * @event beforerowselect
54936              * Fires before a cell is selected.
54937              * @param {SelectionModel} this
54938              * @param {Number} rowIndex The selected row index
54939              * @param {Number} colIndex The selected cell index
54940              */
54941             "beforecellselect" : true,
54942         /**
54943              * @event cellselect
54944              * Fires when a cell is selected.
54945              * @param {SelectionModel} this
54946              * @param {Number} rowIndex The selected row index
54947              * @param {Number} colIndex The selected cell index
54948              */
54949             "cellselect" : true,
54950         /**
54951              * @event selectionchange
54952              * Fires when the active selection changes.
54953              * @param {SelectionModel} this
54954              * @param {Object} selection null for no selection or an object (o) with two properties
54955                 <ul>
54956                 <li>o.record: the record object for the row the selection is in</li>
54957                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54958                 </ul>
54959              */
54960             "selectionchange" : true,
54961         /**
54962              * @event tabend
54963              * Fires when the tab (or enter) was pressed on the last editable cell
54964              * You can use this to trigger add new row.
54965              * @param {SelectionModel} this
54966              */
54967             "tabend" : true,
54968          /**
54969              * @event beforeeditnext
54970              * Fires before the next editable sell is made active
54971              * You can use this to skip to another cell or fire the tabend
54972              *    if you set cell to false
54973              * @param {Object} eventdata object : { cell : [ row, col ] } 
54974              */
54975             "beforeeditnext" : true
54976     });
54977     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54978 };
54979
54980 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54981     
54982     enter_is_tab: false,
54983
54984     /** @ignore */
54985     initEvents : function(){
54986         this.grid.on("mousedown", this.handleMouseDown, this);
54987         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54988         var view = this.grid.view;
54989         view.on("refresh", this.onViewChange, this);
54990         view.on("rowupdated", this.onRowUpdated, this);
54991         view.on("beforerowremoved", this.clearSelections, this);
54992         view.on("beforerowsinserted", this.clearSelections, this);
54993         if(this.grid.isEditor){
54994             this.grid.on("beforeedit", this.beforeEdit,  this);
54995         }
54996     },
54997
54998         //private
54999     beforeEdit : function(e){
55000         this.select(e.row, e.column, false, true, e.record);
55001     },
55002
55003         //private
55004     onRowUpdated : function(v, index, r){
55005         if(this.selection && this.selection.record == r){
55006             v.onCellSelect(index, this.selection.cell[1]);
55007         }
55008     },
55009
55010         //private
55011     onViewChange : function(){
55012         this.clearSelections(true);
55013     },
55014
55015         /**
55016          * Returns the currently selected cell,.
55017          * @return {Array} The selected cell (row, column) or null if none selected.
55018          */
55019     getSelectedCell : function(){
55020         return this.selection ? this.selection.cell : null;
55021     },
55022
55023     /**
55024      * Clears all selections.
55025      * @param {Boolean} true to prevent the gridview from being notified about the change.
55026      */
55027     clearSelections : function(preventNotify){
55028         var s = this.selection;
55029         if(s){
55030             if(preventNotify !== true){
55031                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55032             }
55033             this.selection = null;
55034             this.fireEvent("selectionchange", this, null);
55035         }
55036     },
55037
55038     /**
55039      * Returns true if there is a selection.
55040      * @return {Boolean}
55041      */
55042     hasSelection : function(){
55043         return this.selection ? true : false;
55044     },
55045
55046     /** @ignore */
55047     handleMouseDown : function(e, t){
55048         var v = this.grid.getView();
55049         if(this.isLocked()){
55050             return;
55051         };
55052         var row = v.findRowIndex(t);
55053         var cell = v.findCellIndex(t);
55054         if(row !== false && cell !== false){
55055             this.select(row, cell);
55056         }
55057     },
55058
55059     /**
55060      * Selects a cell.
55061      * @param {Number} rowIndex
55062      * @param {Number} collIndex
55063      */
55064     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55065         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55066             this.clearSelections();
55067             r = r || this.grid.dataSource.getAt(rowIndex);
55068             this.selection = {
55069                 record : r,
55070                 cell : [rowIndex, colIndex]
55071             };
55072             if(!preventViewNotify){
55073                 var v = this.grid.getView();
55074                 v.onCellSelect(rowIndex, colIndex);
55075                 if(preventFocus !== true){
55076                     v.focusCell(rowIndex, colIndex);
55077                 }
55078             }
55079             this.fireEvent("cellselect", this, rowIndex, colIndex);
55080             this.fireEvent("selectionchange", this, this.selection);
55081         }
55082     },
55083
55084         //private
55085     isSelectable : function(rowIndex, colIndex, cm){
55086         return !cm.isHidden(colIndex);
55087     },
55088
55089     /** @ignore */
55090     handleKeyDown : function(e){
55091         //Roo.log('Cell Sel Model handleKeyDown');
55092         if(!e.isNavKeyPress()){
55093             return;
55094         }
55095         var g = this.grid, s = this.selection;
55096         if(!s){
55097             e.stopEvent();
55098             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55099             if(cell){
55100                 this.select(cell[0], cell[1]);
55101             }
55102             return;
55103         }
55104         var sm = this;
55105         var walk = function(row, col, step){
55106             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55107         };
55108         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55109         var newCell;
55110
55111       
55112
55113         switch(k){
55114             case e.TAB:
55115                 // handled by onEditorKey
55116                 if (g.isEditor && g.editing) {
55117                     return;
55118                 }
55119                 if(e.shiftKey) {
55120                     newCell = walk(r, c-1, -1);
55121                 } else {
55122                     newCell = walk(r, c+1, 1);
55123                 }
55124                 break;
55125             
55126             case e.DOWN:
55127                newCell = walk(r+1, c, 1);
55128                 break;
55129             
55130             case e.UP:
55131                 newCell = walk(r-1, c, -1);
55132                 break;
55133             
55134             case e.RIGHT:
55135                 newCell = walk(r, c+1, 1);
55136                 break;
55137             
55138             case e.LEFT:
55139                 newCell = walk(r, c-1, -1);
55140                 break;
55141             
55142             case e.ENTER:
55143                 
55144                 if(g.isEditor && !g.editing){
55145                    g.startEditing(r, c);
55146                    e.stopEvent();
55147                    return;
55148                 }
55149                 
55150                 
55151              break;
55152         };
55153         if(newCell){
55154             this.select(newCell[0], newCell[1]);
55155             e.stopEvent();
55156             
55157         }
55158     },
55159
55160     acceptsNav : function(row, col, cm){
55161         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55162     },
55163     /**
55164      * Selects a cell.
55165      * @param {Number} field (not used) - as it's normally used as a listener
55166      * @param {Number} e - event - fake it by using
55167      *
55168      * var e = Roo.EventObjectImpl.prototype;
55169      * e.keyCode = e.TAB
55170      *
55171      * 
55172      */
55173     onEditorKey : function(field, e){
55174         
55175         var k = e.getKey(),
55176             newCell,
55177             g = this.grid,
55178             ed = g.activeEditor,
55179             forward = false;
55180         ///Roo.log('onEditorKey' + k);
55181         
55182         
55183         if (this.enter_is_tab && k == e.ENTER) {
55184             k = e.TAB;
55185         }
55186         
55187         if(k == e.TAB){
55188             if(e.shiftKey){
55189                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55190             }else{
55191                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55192                 forward = true;
55193             }
55194             
55195             e.stopEvent();
55196             
55197         } else if(k == e.ENTER &&  !e.ctrlKey){
55198             ed.completeEdit();
55199             e.stopEvent();
55200             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55201         
55202                 } else if(k == e.ESC){
55203             ed.cancelEdit();
55204         }
55205                 
55206         if (newCell) {
55207             var ecall = { cell : newCell, forward : forward };
55208             this.fireEvent('beforeeditnext', ecall );
55209             newCell = ecall.cell;
55210                         forward = ecall.forward;
55211         }
55212                 
55213         if(newCell){
55214             //Roo.log('next cell after edit');
55215             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55216         } else if (forward) {
55217             // tabbed past last
55218             this.fireEvent.defer(100, this, ['tabend',this]);
55219         }
55220     }
55221 });/*
55222  * Based on:
55223  * Ext JS Library 1.1.1
55224  * Copyright(c) 2006-2007, Ext JS, LLC.
55225  *
55226  * Originally Released Under LGPL - original licence link has changed is not relivant.
55227  *
55228  * Fork - LGPL
55229  * <script type="text/javascript">
55230  */
55231  
55232 /**
55233  * @class Roo.grid.EditorGrid
55234  * @extends Roo.grid.Grid
55235  * Class for creating and editable grid.
55236  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55237  * The container MUST have some type of size defined for the grid to fill. The container will be 
55238  * automatically set to position relative if it isn't already.
55239  * @param {Object} dataSource The data model to bind to
55240  * @param {Object} colModel The column model with info about this grid's columns
55241  */
55242 Roo.grid.EditorGrid = function(container, config){
55243     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55244     this.getGridEl().addClass("xedit-grid");
55245
55246     if(!this.selModel){
55247         this.selModel = new Roo.grid.CellSelectionModel();
55248     }
55249
55250     this.activeEditor = null;
55251
55252         this.addEvents({
55253             /**
55254              * @event beforeedit
55255              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55256              * <ul style="padding:5px;padding-left:16px;">
55257              * <li>grid - This grid</li>
55258              * <li>record - The record being edited</li>
55259              * <li>field - The field name being edited</li>
55260              * <li>value - The value for the field being edited.</li>
55261              * <li>row - The grid row index</li>
55262              * <li>column - The grid column index</li>
55263              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55264              * </ul>
55265              * @param {Object} e An edit event (see above for description)
55266              */
55267             "beforeedit" : true,
55268             /**
55269              * @event afteredit
55270              * Fires after a cell is edited. <br />
55271              * <ul style="padding:5px;padding-left:16px;">
55272              * <li>grid - This grid</li>
55273              * <li>record - The record being edited</li>
55274              * <li>field - The field name being edited</li>
55275              * <li>value - The value being set</li>
55276              * <li>originalValue - The original value for the field, before the edit.</li>
55277              * <li>row - The grid row index</li>
55278              * <li>column - The grid column index</li>
55279              * </ul>
55280              * @param {Object} e An edit event (see above for description)
55281              */
55282             "afteredit" : true,
55283             /**
55284              * @event validateedit
55285              * Fires after a cell is edited, but before the value is set in the record. 
55286          * You can use this to modify the value being set in the field, Return false
55287              * to cancel the change. The edit event object has the following properties <br />
55288              * <ul style="padding:5px;padding-left:16px;">
55289          * <li>editor - This editor</li>
55290              * <li>grid - This grid</li>
55291              * <li>record - The record being edited</li>
55292              * <li>field - The field name being edited</li>
55293              * <li>value - The value being set</li>
55294              * <li>originalValue - The original value for the field, before the edit.</li>
55295              * <li>row - The grid row index</li>
55296              * <li>column - The grid column index</li>
55297              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55298              * </ul>
55299              * @param {Object} e An edit event (see above for description)
55300              */
55301             "validateedit" : true
55302         });
55303     this.on("bodyscroll", this.stopEditing,  this);
55304     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55305 };
55306
55307 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55308     /**
55309      * @cfg {Number} clicksToEdit
55310      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55311      */
55312     clicksToEdit: 2,
55313
55314     // private
55315     isEditor : true,
55316     // private
55317     trackMouseOver: false, // causes very odd FF errors
55318
55319     onCellDblClick : function(g, row, col){
55320         this.startEditing(row, col);
55321     },
55322
55323     onEditComplete : function(ed, value, startValue){
55324         this.editing = false;
55325         this.activeEditor = null;
55326         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55327         var r = ed.record;
55328         var field = this.colModel.getDataIndex(ed.col);
55329         var e = {
55330             grid: this,
55331             record: r,
55332             field: field,
55333             originalValue: startValue,
55334             value: value,
55335             row: ed.row,
55336             column: ed.col,
55337             cancel:false,
55338             editor: ed
55339         };
55340         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55341         cell.show();
55342           
55343         if(String(value) !== String(startValue)){
55344             
55345             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55346                 r.set(field, e.value);
55347                 // if we are dealing with a combo box..
55348                 // then we also set the 'name' colum to be the displayField
55349                 if (ed.field.displayField && ed.field.name) {
55350                     r.set(ed.field.name, ed.field.el.dom.value);
55351                 }
55352                 
55353                 delete e.cancel; //?? why!!!
55354                 this.fireEvent("afteredit", e);
55355             }
55356         } else {
55357             this.fireEvent("afteredit", e); // always fire it!
55358         }
55359         this.view.focusCell(ed.row, ed.col);
55360     },
55361
55362     /**
55363      * Starts editing the specified for the specified row/column
55364      * @param {Number} rowIndex
55365      * @param {Number} colIndex
55366      */
55367     startEditing : function(row, col){
55368         this.stopEditing();
55369         if(this.colModel.isCellEditable(col, row)){
55370             this.view.ensureVisible(row, col, true);
55371           
55372             var r = this.dataSource.getAt(row);
55373             var field = this.colModel.getDataIndex(col);
55374             var cell = Roo.get(this.view.getCell(row,col));
55375             var e = {
55376                 grid: this,
55377                 record: r,
55378                 field: field,
55379                 value: r.data[field],
55380                 row: row,
55381                 column: col,
55382                 cancel:false 
55383             };
55384             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55385                 this.editing = true;
55386                 var ed = this.colModel.getCellEditor(col, row);
55387                 
55388                 if (!ed) {
55389                     return;
55390                 }
55391                 if(!ed.rendered){
55392                     ed.render(ed.parentEl || document.body);
55393                 }
55394                 ed.field.reset();
55395                
55396                 cell.hide();
55397                 
55398                 (function(){ // complex but required for focus issues in safari, ie and opera
55399                     ed.row = row;
55400                     ed.col = col;
55401                     ed.record = r;
55402                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55403                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55404                     this.activeEditor = ed;
55405                     var v = r.data[field];
55406                     ed.startEdit(this.view.getCell(row, col), v);
55407                     // combo's with 'displayField and name set
55408                     if (ed.field.displayField && ed.field.name) {
55409                         ed.field.el.dom.value = r.data[ed.field.name];
55410                     }
55411                     
55412                     
55413                 }).defer(50, this);
55414             }
55415         }
55416     },
55417         
55418     /**
55419      * Stops any active editing
55420      */
55421     stopEditing : function(){
55422         if(this.activeEditor){
55423             this.activeEditor.completeEdit();
55424         }
55425         this.activeEditor = null;
55426     },
55427         
55428          /**
55429      * Called to get grid's drag proxy text, by default returns this.ddText.
55430      * @return {String}
55431      */
55432     getDragDropText : function(){
55433         var count = this.selModel.getSelectedCell() ? 1 : 0;
55434         return String.format(this.ddText, count, count == 1 ? '' : 's');
55435     }
55436         
55437 });/*
55438  * Based on:
55439  * Ext JS Library 1.1.1
55440  * Copyright(c) 2006-2007, Ext JS, LLC.
55441  *
55442  * Originally Released Under LGPL - original licence link has changed is not relivant.
55443  *
55444  * Fork - LGPL
55445  * <script type="text/javascript">
55446  */
55447
55448 // private - not really -- you end up using it !
55449 // This is a support class used internally by the Grid components
55450
55451 /**
55452  * @class Roo.grid.GridEditor
55453  * @extends Roo.Editor
55454  * Class for creating and editable grid elements.
55455  * @param {Object} config any settings (must include field)
55456  */
55457 Roo.grid.GridEditor = function(field, config){
55458     if (!config && field.field) {
55459         config = field;
55460         field = Roo.factory(config.field, Roo.form);
55461     }
55462     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55463     field.monitorTab = false;
55464 };
55465
55466 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55467     
55468     /**
55469      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55470      */
55471     
55472     alignment: "tl-tl",
55473     autoSize: "width",
55474     hideEl : false,
55475     cls: "x-small-editor x-grid-editor",
55476     shim:false,
55477     shadow:"frame"
55478 });/*
55479  * Based on:
55480  * Ext JS Library 1.1.1
55481  * Copyright(c) 2006-2007, Ext JS, LLC.
55482  *
55483  * Originally Released Under LGPL - original licence link has changed is not relivant.
55484  *
55485  * Fork - LGPL
55486  * <script type="text/javascript">
55487  */
55488   
55489
55490   
55491 Roo.grid.PropertyRecord = Roo.data.Record.create([
55492     {name:'name',type:'string'},  'value'
55493 ]);
55494
55495
55496 Roo.grid.PropertyStore = function(grid, source){
55497     this.grid = grid;
55498     this.store = new Roo.data.Store({
55499         recordType : Roo.grid.PropertyRecord
55500     });
55501     this.store.on('update', this.onUpdate,  this);
55502     if(source){
55503         this.setSource(source);
55504     }
55505     Roo.grid.PropertyStore.superclass.constructor.call(this);
55506 };
55507
55508
55509
55510 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55511     setSource : function(o){
55512         this.source = o;
55513         this.store.removeAll();
55514         var data = [];
55515         for(var k in o){
55516             if(this.isEditableValue(o[k])){
55517                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55518             }
55519         }
55520         this.store.loadRecords({records: data}, {}, true);
55521     },
55522
55523     onUpdate : function(ds, record, type){
55524         if(type == Roo.data.Record.EDIT){
55525             var v = record.data['value'];
55526             var oldValue = record.modified['value'];
55527             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55528                 this.source[record.id] = v;
55529                 record.commit();
55530                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55531             }else{
55532                 record.reject();
55533             }
55534         }
55535     },
55536
55537     getProperty : function(row){
55538        return this.store.getAt(row);
55539     },
55540
55541     isEditableValue: function(val){
55542         if(val && val instanceof Date){
55543             return true;
55544         }else if(typeof val == 'object' || typeof val == 'function'){
55545             return false;
55546         }
55547         return true;
55548     },
55549
55550     setValue : function(prop, value){
55551         this.source[prop] = value;
55552         this.store.getById(prop).set('value', value);
55553     },
55554
55555     getSource : function(){
55556         return this.source;
55557     }
55558 });
55559
55560 Roo.grid.PropertyColumnModel = function(grid, store){
55561     this.grid = grid;
55562     var g = Roo.grid;
55563     g.PropertyColumnModel.superclass.constructor.call(this, [
55564         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55565         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55566     ]);
55567     this.store = store;
55568     this.bselect = Roo.DomHelper.append(document.body, {
55569         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55570             {tag: 'option', value: 'true', html: 'true'},
55571             {tag: 'option', value: 'false', html: 'false'}
55572         ]
55573     });
55574     Roo.id(this.bselect);
55575     var f = Roo.form;
55576     this.editors = {
55577         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55578         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55579         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55580         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55581         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55582     };
55583     this.renderCellDelegate = this.renderCell.createDelegate(this);
55584     this.renderPropDelegate = this.renderProp.createDelegate(this);
55585 };
55586
55587 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55588     
55589     
55590     nameText : 'Name',
55591     valueText : 'Value',
55592     
55593     dateFormat : 'm/j/Y',
55594     
55595     
55596     renderDate : function(dateVal){
55597         return dateVal.dateFormat(this.dateFormat);
55598     },
55599
55600     renderBool : function(bVal){
55601         return bVal ? 'true' : 'false';
55602     },
55603
55604     isCellEditable : function(colIndex, rowIndex){
55605         return colIndex == 1;
55606     },
55607
55608     getRenderer : function(col){
55609         return col == 1 ?
55610             this.renderCellDelegate : this.renderPropDelegate;
55611     },
55612
55613     renderProp : function(v){
55614         return this.getPropertyName(v);
55615     },
55616
55617     renderCell : function(val){
55618         var rv = val;
55619         if(val instanceof Date){
55620             rv = this.renderDate(val);
55621         }else if(typeof val == 'boolean'){
55622             rv = this.renderBool(val);
55623         }
55624         return Roo.util.Format.htmlEncode(rv);
55625     },
55626
55627     getPropertyName : function(name){
55628         var pn = this.grid.propertyNames;
55629         return pn && pn[name] ? pn[name] : name;
55630     },
55631
55632     getCellEditor : function(colIndex, rowIndex){
55633         var p = this.store.getProperty(rowIndex);
55634         var n = p.data['name'], val = p.data['value'];
55635         
55636         if(typeof(this.grid.customEditors[n]) == 'string'){
55637             return this.editors[this.grid.customEditors[n]];
55638         }
55639         if(typeof(this.grid.customEditors[n]) != 'undefined'){
55640             return this.grid.customEditors[n];
55641         }
55642         if(val instanceof Date){
55643             return this.editors['date'];
55644         }else if(typeof val == 'number'){
55645             return this.editors['number'];
55646         }else if(typeof val == 'boolean'){
55647             return this.editors['boolean'];
55648         }else{
55649             return this.editors['string'];
55650         }
55651     }
55652 });
55653
55654 /**
55655  * @class Roo.grid.PropertyGrid
55656  * @extends Roo.grid.EditorGrid
55657  * This class represents the  interface of a component based property grid control.
55658  * <br><br>Usage:<pre><code>
55659  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55660       
55661  });
55662  // set any options
55663  grid.render();
55664  * </code></pre>
55665   
55666  * @constructor
55667  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55668  * The container MUST have some type of size defined for the grid to fill. The container will be
55669  * automatically set to position relative if it isn't already.
55670  * @param {Object} config A config object that sets properties on this grid.
55671  */
55672 Roo.grid.PropertyGrid = function(container, config){
55673     config = config || {};
55674     var store = new Roo.grid.PropertyStore(this);
55675     this.store = store;
55676     var cm = new Roo.grid.PropertyColumnModel(this, store);
55677     store.store.sort('name', 'ASC');
55678     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55679         ds: store.store,
55680         cm: cm,
55681         enableColLock:false,
55682         enableColumnMove:false,
55683         stripeRows:false,
55684         trackMouseOver: false,
55685         clicksToEdit:1
55686     }, config));
55687     this.getGridEl().addClass('x-props-grid');
55688     this.lastEditRow = null;
55689     this.on('columnresize', this.onColumnResize, this);
55690     this.addEvents({
55691          /**
55692              * @event beforepropertychange
55693              * Fires before a property changes (return false to stop?)
55694              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55695              * @param {String} id Record Id
55696              * @param {String} newval New Value
55697          * @param {String} oldval Old Value
55698              */
55699         "beforepropertychange": true,
55700         /**
55701              * @event propertychange
55702              * Fires after a property changes
55703              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55704              * @param {String} id Record Id
55705              * @param {String} newval New Value
55706          * @param {String} oldval Old Value
55707              */
55708         "propertychange": true
55709     });
55710     this.customEditors = this.customEditors || {};
55711 };
55712 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55713     
55714      /**
55715      * @cfg {Object} customEditors map of colnames=> custom editors.
55716      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55717      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55718      * false disables editing of the field.
55719          */
55720     
55721       /**
55722      * @cfg {Object} propertyNames map of property Names to their displayed value
55723          */
55724     
55725     render : function(){
55726         Roo.grid.PropertyGrid.superclass.render.call(this);
55727         this.autoSize.defer(100, this);
55728     },
55729
55730     autoSize : function(){
55731         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55732         if(this.view){
55733             this.view.fitColumns();
55734         }
55735     },
55736
55737     onColumnResize : function(){
55738         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55739         this.autoSize();
55740     },
55741     /**
55742      * Sets the data for the Grid
55743      * accepts a Key => Value object of all the elements avaiable.
55744      * @param {Object} data  to appear in grid.
55745      */
55746     setSource : function(source){
55747         this.store.setSource(source);
55748         //this.autoSize();
55749     },
55750     /**
55751      * Gets all the data from the grid.
55752      * @return {Object} data  data stored in grid
55753      */
55754     getSource : function(){
55755         return this.store.getSource();
55756     }
55757 });/*
55758   
55759  * Licence LGPL
55760  
55761  */
55762  
55763 /**
55764  * @class Roo.grid.Calendar
55765  * @extends Roo.util.Grid
55766  * This class extends the Grid to provide a calendar widget
55767  * <br><br>Usage:<pre><code>
55768  var grid = new Roo.grid.Calendar("my-container-id", {
55769      ds: myDataStore,
55770      cm: myColModel,
55771      selModel: mySelectionModel,
55772      autoSizeColumns: true,
55773      monitorWindowResize: false,
55774      trackMouseOver: true
55775      eventstore : real data store..
55776  });
55777  // set any options
55778  grid.render();
55779   
55780   * @constructor
55781  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55782  * The container MUST have some type of size defined for the grid to fill. The container will be
55783  * automatically set to position relative if it isn't already.
55784  * @param {Object} config A config object that sets properties on this grid.
55785  */
55786 Roo.grid.Calendar = function(container, config){
55787         // initialize the container
55788         this.container = Roo.get(container);
55789         this.container.update("");
55790         this.container.setStyle("overflow", "hidden");
55791     this.container.addClass('x-grid-container');
55792
55793     this.id = this.container.id;
55794
55795     Roo.apply(this, config);
55796     // check and correct shorthanded configs
55797     
55798     var rows = [];
55799     var d =1;
55800     for (var r = 0;r < 6;r++) {
55801         
55802         rows[r]=[];
55803         for (var c =0;c < 7;c++) {
55804             rows[r][c]= '';
55805         }
55806     }
55807     if (this.eventStore) {
55808         this.eventStore= Roo.factory(this.eventStore, Roo.data);
55809         this.eventStore.on('load',this.onLoad, this);
55810         this.eventStore.on('beforeload',this.clearEvents, this);
55811          
55812     }
55813     
55814     this.dataSource = new Roo.data.Store({
55815             proxy: new Roo.data.MemoryProxy(rows),
55816             reader: new Roo.data.ArrayReader({}, [
55817                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
55818     });
55819
55820     this.dataSource.load();
55821     this.ds = this.dataSource;
55822     this.ds.xmodule = this.xmodule || false;
55823     
55824     
55825     var cellRender = function(v,x,r)
55826     {
55827         return String.format(
55828             '<div class="fc-day  fc-widget-content"><div>' +
55829                 '<div class="fc-event-container"></div>' +
55830                 '<div class="fc-day-number">{0}</div>'+
55831                 
55832                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
55833             '</div></div>', v);
55834     
55835     }
55836     
55837     
55838     this.colModel = new Roo.grid.ColumnModel( [
55839         {
55840             xtype: 'ColumnModel',
55841             xns: Roo.grid,
55842             dataIndex : 'weekday0',
55843             header : 'Sunday',
55844             renderer : cellRender
55845         },
55846         {
55847             xtype: 'ColumnModel',
55848             xns: Roo.grid,
55849             dataIndex : 'weekday1',
55850             header : 'Monday',
55851             renderer : cellRender
55852         },
55853         {
55854             xtype: 'ColumnModel',
55855             xns: Roo.grid,
55856             dataIndex : 'weekday2',
55857             header : 'Tuesday',
55858             renderer : cellRender
55859         },
55860         {
55861             xtype: 'ColumnModel',
55862             xns: Roo.grid,
55863             dataIndex : 'weekday3',
55864             header : 'Wednesday',
55865             renderer : cellRender
55866         },
55867         {
55868             xtype: 'ColumnModel',
55869             xns: Roo.grid,
55870             dataIndex : 'weekday4',
55871             header : 'Thursday',
55872             renderer : cellRender
55873         },
55874         {
55875             xtype: 'ColumnModel',
55876             xns: Roo.grid,
55877             dataIndex : 'weekday5',
55878             header : 'Friday',
55879             renderer : cellRender
55880         },
55881         {
55882             xtype: 'ColumnModel',
55883             xns: Roo.grid,
55884             dataIndex : 'weekday6',
55885             header : 'Saturday',
55886             renderer : cellRender
55887         }
55888     ]);
55889     this.cm = this.colModel;
55890     this.cm.xmodule = this.xmodule || false;
55891  
55892         
55893           
55894     //this.selModel = new Roo.grid.CellSelectionModel();
55895     //this.sm = this.selModel;
55896     //this.selModel.init(this);
55897     
55898     
55899     if(this.width){
55900         this.container.setWidth(this.width);
55901     }
55902
55903     if(this.height){
55904         this.container.setHeight(this.height);
55905     }
55906     /** @private */
55907         this.addEvents({
55908         // raw events
55909         /**
55910          * @event click
55911          * The raw click event for the entire grid.
55912          * @param {Roo.EventObject} e
55913          */
55914         "click" : true,
55915         /**
55916          * @event dblclick
55917          * The raw dblclick event for the entire grid.
55918          * @param {Roo.EventObject} e
55919          */
55920         "dblclick" : true,
55921         /**
55922          * @event contextmenu
55923          * The raw contextmenu event for the entire grid.
55924          * @param {Roo.EventObject} e
55925          */
55926         "contextmenu" : true,
55927         /**
55928          * @event mousedown
55929          * The raw mousedown event for the entire grid.
55930          * @param {Roo.EventObject} e
55931          */
55932         "mousedown" : true,
55933         /**
55934          * @event mouseup
55935          * The raw mouseup event for the entire grid.
55936          * @param {Roo.EventObject} e
55937          */
55938         "mouseup" : true,
55939         /**
55940          * @event mouseover
55941          * The raw mouseover event for the entire grid.
55942          * @param {Roo.EventObject} e
55943          */
55944         "mouseover" : true,
55945         /**
55946          * @event mouseout
55947          * The raw mouseout event for the entire grid.
55948          * @param {Roo.EventObject} e
55949          */
55950         "mouseout" : true,
55951         /**
55952          * @event keypress
55953          * The raw keypress event for the entire grid.
55954          * @param {Roo.EventObject} e
55955          */
55956         "keypress" : true,
55957         /**
55958          * @event keydown
55959          * The raw keydown event for the entire grid.
55960          * @param {Roo.EventObject} e
55961          */
55962         "keydown" : true,
55963
55964         // custom events
55965
55966         /**
55967          * @event cellclick
55968          * Fires when a cell is clicked
55969          * @param {Grid} this
55970          * @param {Number} rowIndex
55971          * @param {Number} columnIndex
55972          * @param {Roo.EventObject} e
55973          */
55974         "cellclick" : true,
55975         /**
55976          * @event celldblclick
55977          * Fires when a cell is double clicked
55978          * @param {Grid} this
55979          * @param {Number} rowIndex
55980          * @param {Number} columnIndex
55981          * @param {Roo.EventObject} e
55982          */
55983         "celldblclick" : true,
55984         /**
55985          * @event rowclick
55986          * Fires when a row is clicked
55987          * @param {Grid} this
55988          * @param {Number} rowIndex
55989          * @param {Roo.EventObject} e
55990          */
55991         "rowclick" : true,
55992         /**
55993          * @event rowdblclick
55994          * Fires when a row is double clicked
55995          * @param {Grid} this
55996          * @param {Number} rowIndex
55997          * @param {Roo.EventObject} e
55998          */
55999         "rowdblclick" : true,
56000         /**
56001          * @event headerclick
56002          * Fires when a header is clicked
56003          * @param {Grid} this
56004          * @param {Number} columnIndex
56005          * @param {Roo.EventObject} e
56006          */
56007         "headerclick" : true,
56008         /**
56009          * @event headerdblclick
56010          * Fires when a header cell is double clicked
56011          * @param {Grid} this
56012          * @param {Number} columnIndex
56013          * @param {Roo.EventObject} e
56014          */
56015         "headerdblclick" : true,
56016         /**
56017          * @event rowcontextmenu
56018          * Fires when a row is right clicked
56019          * @param {Grid} this
56020          * @param {Number} rowIndex
56021          * @param {Roo.EventObject} e
56022          */
56023         "rowcontextmenu" : true,
56024         /**
56025          * @event cellcontextmenu
56026          * Fires when a cell is right clicked
56027          * @param {Grid} this
56028          * @param {Number} rowIndex
56029          * @param {Number} cellIndex
56030          * @param {Roo.EventObject} e
56031          */
56032          "cellcontextmenu" : true,
56033         /**
56034          * @event headercontextmenu
56035          * Fires when a header is right clicked
56036          * @param {Grid} this
56037          * @param {Number} columnIndex
56038          * @param {Roo.EventObject} e
56039          */
56040         "headercontextmenu" : true,
56041         /**
56042          * @event bodyscroll
56043          * Fires when the body element is scrolled
56044          * @param {Number} scrollLeft
56045          * @param {Number} scrollTop
56046          */
56047         "bodyscroll" : true,
56048         /**
56049          * @event columnresize
56050          * Fires when the user resizes a column
56051          * @param {Number} columnIndex
56052          * @param {Number} newSize
56053          */
56054         "columnresize" : true,
56055         /**
56056          * @event columnmove
56057          * Fires when the user moves a column
56058          * @param {Number} oldIndex
56059          * @param {Number} newIndex
56060          */
56061         "columnmove" : true,
56062         /**
56063          * @event startdrag
56064          * Fires when row(s) start being dragged
56065          * @param {Grid} this
56066          * @param {Roo.GridDD} dd The drag drop object
56067          * @param {event} e The raw browser event
56068          */
56069         "startdrag" : true,
56070         /**
56071          * @event enddrag
56072          * Fires when a drag operation is complete
56073          * @param {Grid} this
56074          * @param {Roo.GridDD} dd The drag drop object
56075          * @param {event} e The raw browser event
56076          */
56077         "enddrag" : true,
56078         /**
56079          * @event dragdrop
56080          * Fires when dragged row(s) are dropped on a valid DD target
56081          * @param {Grid} this
56082          * @param {Roo.GridDD} dd The drag drop object
56083          * @param {String} targetId The target drag drop object
56084          * @param {event} e The raw browser event
56085          */
56086         "dragdrop" : true,
56087         /**
56088          * @event dragover
56089          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56090          * @param {Grid} this
56091          * @param {Roo.GridDD} dd The drag drop object
56092          * @param {String} targetId The target drag drop object
56093          * @param {event} e The raw browser event
56094          */
56095         "dragover" : true,
56096         /**
56097          * @event dragenter
56098          *  Fires when the dragged row(s) first cross another DD target while being dragged
56099          * @param {Grid} this
56100          * @param {Roo.GridDD} dd The drag drop object
56101          * @param {String} targetId The target drag drop object
56102          * @param {event} e The raw browser event
56103          */
56104         "dragenter" : true,
56105         /**
56106          * @event dragout
56107          * Fires when the dragged row(s) leave another DD target while being dragged
56108          * @param {Grid} this
56109          * @param {Roo.GridDD} dd The drag drop object
56110          * @param {String} targetId The target drag drop object
56111          * @param {event} e The raw browser event
56112          */
56113         "dragout" : true,
56114         /**
56115          * @event rowclass
56116          * Fires when a row is rendered, so you can change add a style to it.
56117          * @param {GridView} gridview   The grid view
56118          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56119          */
56120         'rowclass' : true,
56121
56122         /**
56123          * @event render
56124          * Fires when the grid is rendered
56125          * @param {Grid} grid
56126          */
56127         'render' : true,
56128             /**
56129              * @event select
56130              * Fires when a date is selected
56131              * @param {DatePicker} this
56132              * @param {Date} date The selected date
56133              */
56134         'select': true,
56135         /**
56136              * @event monthchange
56137              * Fires when the displayed month changes 
56138              * @param {DatePicker} this
56139              * @param {Date} date The selected month
56140              */
56141         'monthchange': true,
56142         /**
56143              * @event evententer
56144              * Fires when mouse over an event
56145              * @param {Calendar} this
56146              * @param {event} Event
56147              */
56148         'evententer': true,
56149         /**
56150              * @event eventleave
56151              * Fires when the mouse leaves an
56152              * @param {Calendar} this
56153              * @param {event}
56154              */
56155         'eventleave': true,
56156         /**
56157              * @event eventclick
56158              * Fires when the mouse click an
56159              * @param {Calendar} this
56160              * @param {event}
56161              */
56162         'eventclick': true,
56163         /**
56164              * @event eventrender
56165              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56166              * @param {Calendar} this
56167              * @param {data} data to be modified
56168              */
56169         'eventrender': true
56170         
56171     });
56172
56173     Roo.grid.Grid.superclass.constructor.call(this);
56174     this.on('render', function() {
56175         this.view.el.addClass('x-grid-cal'); 
56176         
56177         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56178
56179     },this);
56180     
56181     if (!Roo.grid.Calendar.style) {
56182         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56183             
56184             
56185             '.x-grid-cal .x-grid-col' :  {
56186                 height: 'auto !important',
56187                 'vertical-align': 'top'
56188             },
56189             '.x-grid-cal  .fc-event-hori' : {
56190                 height: '14px'
56191             }
56192              
56193             
56194         }, Roo.id());
56195     }
56196
56197     
56198     
56199 };
56200 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56201     /**
56202      * @cfg {Store} eventStore The store that loads events.
56203      */
56204     eventStore : 25,
56205
56206      
56207     activeDate : false,
56208     startDay : 0,
56209     autoWidth : true,
56210     monitorWindowResize : false,
56211
56212     
56213     resizeColumns : function() {
56214         var col = (this.view.el.getWidth() / 7) - 3;
56215         // loop through cols, and setWidth
56216         for(var i =0 ; i < 7 ; i++){
56217             this.cm.setColumnWidth(i, col);
56218         }
56219     },
56220      setDate :function(date) {
56221         
56222         Roo.log('setDate?');
56223         
56224         this.resizeColumns();
56225         var vd = this.activeDate;
56226         this.activeDate = date;
56227 //        if(vd && this.el){
56228 //            var t = date.getTime();
56229 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56230 //                Roo.log('using add remove');
56231 //                
56232 //                this.fireEvent('monthchange', this, date);
56233 //                
56234 //                this.cells.removeClass("fc-state-highlight");
56235 //                this.cells.each(function(c){
56236 //                   if(c.dateValue == t){
56237 //                       c.addClass("fc-state-highlight");
56238 //                       setTimeout(function(){
56239 //                            try{c.dom.firstChild.focus();}catch(e){}
56240 //                       }, 50);
56241 //                       return false;
56242 //                   }
56243 //                   return true;
56244 //                });
56245 //                return;
56246 //            }
56247 //        }
56248         
56249         var days = date.getDaysInMonth();
56250         
56251         var firstOfMonth = date.getFirstDateOfMonth();
56252         var startingPos = firstOfMonth.getDay()-this.startDay;
56253         
56254         if(startingPos < this.startDay){
56255             startingPos += 7;
56256         }
56257         
56258         var pm = date.add(Date.MONTH, -1);
56259         var prevStart = pm.getDaysInMonth()-startingPos;
56260 //        
56261         
56262         
56263         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56264         
56265         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
56266         //this.cells.addClassOnOver('fc-state-hover');
56267         
56268         var cells = this.cells.elements;
56269         var textEls = this.textNodes;
56270         
56271         //Roo.each(cells, function(cell){
56272         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
56273         //});
56274         
56275         days += startingPos;
56276
56277         // convert everything to numbers so it's fast
56278         var day = 86400000;
56279         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
56280         //Roo.log(d);
56281         //Roo.log(pm);
56282         //Roo.log(prevStart);
56283         
56284         var today = new Date().clearTime().getTime();
56285         var sel = date.clearTime().getTime();
56286         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
56287         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
56288         var ddMatch = this.disabledDatesRE;
56289         var ddText = this.disabledDatesText;
56290         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
56291         var ddaysText = this.disabledDaysText;
56292         var format = this.format;
56293         
56294         var setCellClass = function(cal, cell){
56295             
56296             //Roo.log('set Cell Class');
56297             cell.title = "";
56298             var t = d.getTime();
56299             
56300             //Roo.log(d);
56301             
56302             
56303             cell.dateValue = t;
56304             if(t == today){
56305                 cell.className += " fc-today";
56306                 cell.className += " fc-state-highlight";
56307                 cell.title = cal.todayText;
56308             }
56309             if(t == sel){
56310                 // disable highlight in other month..
56311                 cell.className += " fc-state-highlight";
56312                 
56313             }
56314             // disabling
56315             if(t < min) {
56316                 //cell.className = " fc-state-disabled";
56317                 cell.title = cal.minText;
56318                 return;
56319             }
56320             if(t > max) {
56321                 //cell.className = " fc-state-disabled";
56322                 cell.title = cal.maxText;
56323                 return;
56324             }
56325             if(ddays){
56326                 if(ddays.indexOf(d.getDay()) != -1){
56327                     // cell.title = ddaysText;
56328                    // cell.className = " fc-state-disabled";
56329                 }
56330             }
56331             if(ddMatch && format){
56332                 var fvalue = d.dateFormat(format);
56333                 if(ddMatch.test(fvalue)){
56334                     cell.title = ddText.replace("%0", fvalue);
56335                    cell.className = " fc-state-disabled";
56336                 }
56337             }
56338             
56339             if (!cell.initialClassName) {
56340                 cell.initialClassName = cell.dom.className;
56341             }
56342             
56343             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
56344         };
56345
56346         var i = 0;
56347         
56348         for(; i < startingPos; i++) {
56349             cells[i].dayName =  (++prevStart);
56350             Roo.log(textEls[i]);
56351             d.setDate(d.getDate()+1);
56352             
56353             //cells[i].className = "fc-past fc-other-month";
56354             setCellClass(this, cells[i]);
56355         }
56356         
56357         var intDay = 0;
56358         
56359         for(; i < days; i++){
56360             intDay = i - startingPos + 1;
56361             cells[i].dayName =  (intDay);
56362             d.setDate(d.getDate()+1);
56363             
56364             cells[i].className = ''; // "x-date-active";
56365             setCellClass(this, cells[i]);
56366         }
56367         var extraDays = 0;
56368         
56369         for(; i < 42; i++) {
56370             //textEls[i].innerHTML = (++extraDays);
56371             
56372             d.setDate(d.getDate()+1);
56373             cells[i].dayName = (++extraDays);
56374             cells[i].className = "fc-future fc-other-month";
56375             setCellClass(this, cells[i]);
56376         }
56377         
56378         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
56379         
56380         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
56381         
56382         // this will cause all the cells to mis
56383         var rows= [];
56384         var i =0;
56385         for (var r = 0;r < 6;r++) {
56386             for (var c =0;c < 7;c++) {
56387                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
56388             }    
56389         }
56390         
56391         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56392         for(i=0;i<cells.length;i++) {
56393             
56394             this.cells.elements[i].dayName = cells[i].dayName ;
56395             this.cells.elements[i].className = cells[i].className;
56396             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
56397             this.cells.elements[i].title = cells[i].title ;
56398             this.cells.elements[i].dateValue = cells[i].dateValue ;
56399         }
56400         
56401         
56402         
56403         
56404         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
56405         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
56406         
56407         ////if(totalRows != 6){
56408             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
56409            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
56410        // }
56411         
56412         this.fireEvent('monthchange', this, date);
56413         
56414         
56415     },
56416  /**
56417      * Returns the grid's SelectionModel.
56418      * @return {SelectionModel}
56419      */
56420     getSelectionModel : function(){
56421         if(!this.selModel){
56422             this.selModel = new Roo.grid.CellSelectionModel();
56423         }
56424         return this.selModel;
56425     },
56426
56427     load: function() {
56428         this.eventStore.load()
56429         
56430         
56431         
56432     },
56433     
56434     findCell : function(dt) {
56435         dt = dt.clearTime().getTime();
56436         var ret = false;
56437         this.cells.each(function(c){
56438             //Roo.log("check " +c.dateValue + '?=' + dt);
56439             if(c.dateValue == dt){
56440                 ret = c;
56441                 return false;
56442             }
56443             return true;
56444         });
56445         
56446         return ret;
56447     },
56448     
56449     findCells : function(rec) {
56450         var s = rec.data.start_dt.clone().clearTime().getTime();
56451        // Roo.log(s);
56452         var e= rec.data.end_dt.clone().clearTime().getTime();
56453        // Roo.log(e);
56454         var ret = [];
56455         this.cells.each(function(c){
56456              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
56457             
56458             if(c.dateValue > e){
56459                 return ;
56460             }
56461             if(c.dateValue < s){
56462                 return ;
56463             }
56464             ret.push(c);
56465         });
56466         
56467         return ret;    
56468     },
56469     
56470     findBestRow: function(cells)
56471     {
56472         var ret = 0;
56473         
56474         for (var i =0 ; i < cells.length;i++) {
56475             ret  = Math.max(cells[i].rows || 0,ret);
56476         }
56477         return ret;
56478         
56479     },
56480     
56481     
56482     addItem : function(rec)
56483     {
56484         // look for vertical location slot in
56485         var cells = this.findCells(rec);
56486         
56487         rec.row = this.findBestRow(cells);
56488         
56489         // work out the location.
56490         
56491         var crow = false;
56492         var rows = [];
56493         for(var i =0; i < cells.length; i++) {
56494             if (!crow) {
56495                 crow = {
56496                     start : cells[i],
56497                     end :  cells[i]
56498                 };
56499                 continue;
56500             }
56501             if (crow.start.getY() == cells[i].getY()) {
56502                 // on same row.
56503                 crow.end = cells[i];
56504                 continue;
56505             }
56506             // different row.
56507             rows.push(crow);
56508             crow = {
56509                 start: cells[i],
56510                 end : cells[i]
56511             };
56512             
56513         }
56514         
56515         rows.push(crow);
56516         rec.els = [];
56517         rec.rows = rows;
56518         rec.cells = cells;
56519         for (var i = 0; i < cells.length;i++) {
56520             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
56521             
56522         }
56523         
56524         
56525     },
56526     
56527     clearEvents: function() {
56528         
56529         if (!this.eventStore.getCount()) {
56530             return;
56531         }
56532         // reset number of rows in cells.
56533         Roo.each(this.cells.elements, function(c){
56534             c.rows = 0;
56535         });
56536         
56537         this.eventStore.each(function(e) {
56538             this.clearEvent(e);
56539         },this);
56540         
56541     },
56542     
56543     clearEvent : function(ev)
56544     {
56545         if (ev.els) {
56546             Roo.each(ev.els, function(el) {
56547                 el.un('mouseenter' ,this.onEventEnter, this);
56548                 el.un('mouseleave' ,this.onEventLeave, this);
56549                 el.remove();
56550             },this);
56551             ev.els = [];
56552         }
56553     },
56554     
56555     
56556     renderEvent : function(ev,ctr) {
56557         if (!ctr) {
56558              ctr = this.view.el.select('.fc-event-container',true).first();
56559         }
56560         
56561          
56562         this.clearEvent(ev);
56563             //code
56564        
56565         
56566         
56567         ev.els = [];
56568         var cells = ev.cells;
56569         var rows = ev.rows;
56570         this.fireEvent('eventrender', this, ev);
56571         
56572         for(var i =0; i < rows.length; i++) {
56573             
56574             cls = '';
56575             if (i == 0) {
56576                 cls += ' fc-event-start';
56577             }
56578             if ((i+1) == rows.length) {
56579                 cls += ' fc-event-end';
56580             }
56581             
56582             //Roo.log(ev.data);
56583             // how many rows should it span..
56584             var cg = this.eventTmpl.append(ctr,Roo.apply({
56585                 fccls : cls
56586                 
56587             }, ev.data) , true);
56588             
56589             
56590             cg.on('mouseenter' ,this.onEventEnter, this, ev);
56591             cg.on('mouseleave' ,this.onEventLeave, this, ev);
56592             cg.on('click', this.onEventClick, this, ev);
56593             
56594             ev.els.push(cg);
56595             
56596             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
56597             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
56598             //Roo.log(cg);
56599              
56600             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
56601             cg.setWidth(ebox.right - sbox.x -2);
56602         }
56603     },
56604     
56605     renderEvents: function()
56606     {   
56607         // first make sure there is enough space..
56608         
56609         if (!this.eventTmpl) {
56610             this.eventTmpl = new Roo.Template(
56611                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
56612                     '<div class="fc-event-inner">' +
56613                         '<span class="fc-event-time">{time}</span>' +
56614                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
56615                     '</div>' +
56616                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
56617                 '</div>'
56618             );
56619                 
56620         }
56621                
56622         
56623         
56624         this.cells.each(function(c) {
56625             //Roo.log(c.select('.fc-day-content div',true).first());
56626             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
56627         });
56628         
56629         var ctr = this.view.el.select('.fc-event-container',true).first();
56630         
56631         var cls;
56632         this.eventStore.each(function(ev){
56633             
56634             this.renderEvent(ev);
56635              
56636              
56637         }, this);
56638         this.view.layout();
56639         
56640     },
56641     
56642     onEventEnter: function (e, el,event,d) {
56643         this.fireEvent('evententer', this, el, event);
56644     },
56645     
56646     onEventLeave: function (e, el,event,d) {
56647         this.fireEvent('eventleave', this, el, event);
56648     },
56649     
56650     onEventClick: function (e, el,event,d) {
56651         this.fireEvent('eventclick', this, el, event);
56652     },
56653     
56654     onMonthChange: function () {
56655         this.store.load();
56656     },
56657     
56658     onLoad: function () {
56659         
56660         //Roo.log('calendar onload');
56661 //         
56662         if(this.eventStore.getCount() > 0){
56663             
56664            
56665             
56666             this.eventStore.each(function(d){
56667                 
56668                 
56669                 // FIXME..
56670                 var add =   d.data;
56671                 if (typeof(add.end_dt) == 'undefined')  {
56672                     Roo.log("Missing End time in calendar data: ");
56673                     Roo.log(d);
56674                     return;
56675                 }
56676                 if (typeof(add.start_dt) == 'undefined')  {
56677                     Roo.log("Missing Start time in calendar data: ");
56678                     Roo.log(d);
56679                     return;
56680                 }
56681                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
56682                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
56683                 add.id = add.id || d.id;
56684                 add.title = add.title || '??';
56685                 
56686                 this.addItem(d);
56687                 
56688              
56689             },this);
56690         }
56691         
56692         this.renderEvents();
56693     }
56694     
56695
56696 });
56697 /*
56698  grid : {
56699                 xtype: 'Grid',
56700                 xns: Roo.grid,
56701                 listeners : {
56702                     render : function ()
56703                     {
56704                         _this.grid = this;
56705                         
56706                         if (!this.view.el.hasClass('course-timesheet')) {
56707                             this.view.el.addClass('course-timesheet');
56708                         }
56709                         if (this.tsStyle) {
56710                             this.ds.load({});
56711                             return; 
56712                         }
56713                         Roo.log('width');
56714                         Roo.log(_this.grid.view.el.getWidth());
56715                         
56716                         
56717                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
56718                             '.course-timesheet .x-grid-row' : {
56719                                 height: '80px'
56720                             },
56721                             '.x-grid-row td' : {
56722                                 'vertical-align' : 0
56723                             },
56724                             '.course-edit-link' : {
56725                                 'color' : 'blue',
56726                                 'text-overflow' : 'ellipsis',
56727                                 'overflow' : 'hidden',
56728                                 'white-space' : 'nowrap',
56729                                 'cursor' : 'pointer'
56730                             },
56731                             '.sub-link' : {
56732                                 'color' : 'green'
56733                             },
56734                             '.de-act-sup-link' : {
56735                                 'color' : 'purple',
56736                                 'text-decoration' : 'line-through'
56737                             },
56738                             '.de-act-link' : {
56739                                 'color' : 'red',
56740                                 'text-decoration' : 'line-through'
56741                             },
56742                             '.course-timesheet .course-highlight' : {
56743                                 'border-top-style': 'dashed !important',
56744                                 'border-bottom-bottom': 'dashed !important'
56745                             },
56746                             '.course-timesheet .course-item' : {
56747                                 'font-family'   : 'tahoma, arial, helvetica',
56748                                 'font-size'     : '11px',
56749                                 'overflow'      : 'hidden',
56750                                 'padding-left'  : '10px',
56751                                 'padding-right' : '10px',
56752                                 'padding-top' : '10px' 
56753                             }
56754                             
56755                         }, Roo.id());
56756                                 this.ds.load({});
56757                     }
56758                 },
56759                 autoWidth : true,
56760                 monitorWindowResize : false,
56761                 cellrenderer : function(v,x,r)
56762                 {
56763                     return v;
56764                 },
56765                 sm : {
56766                     xtype: 'CellSelectionModel',
56767                     xns: Roo.grid
56768                 },
56769                 dataSource : {
56770                     xtype: 'Store',
56771                     xns: Roo.data,
56772                     listeners : {
56773                         beforeload : function (_self, options)
56774                         {
56775                             options.params = options.params || {};
56776                             options.params._month = _this.monthField.getValue();
56777                             options.params.limit = 9999;
56778                             options.params['sort'] = 'when_dt';    
56779                             options.params['dir'] = 'ASC';    
56780                             this.proxy.loadResponse = this.loadResponse;
56781                             Roo.log("load?");
56782                             //this.addColumns();
56783                         },
56784                         load : function (_self, records, options)
56785                         {
56786                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
56787                                 // if you click on the translation.. you can edit it...
56788                                 var el = Roo.get(this);
56789                                 var id = el.dom.getAttribute('data-id');
56790                                 var d = el.dom.getAttribute('data-date');
56791                                 var t = el.dom.getAttribute('data-time');
56792                                 //var id = this.child('span').dom.textContent;
56793                                 
56794                                 //Roo.log(this);
56795                                 Pman.Dialog.CourseCalendar.show({
56796                                     id : id,
56797                                     when_d : d,
56798                                     when_t : t,
56799                                     productitem_active : id ? 1 : 0
56800                                 }, function() {
56801                                     _this.grid.ds.load({});
56802                                 });
56803                            
56804                            });
56805                            
56806                            _this.panel.fireEvent('resize', [ '', '' ]);
56807                         }
56808                     },
56809                     loadResponse : function(o, success, response){
56810                             // this is overridden on before load..
56811                             
56812                             Roo.log("our code?");       
56813                             //Roo.log(success);
56814                             //Roo.log(response)
56815                             delete this.activeRequest;
56816                             if(!success){
56817                                 this.fireEvent("loadexception", this, o, response);
56818                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
56819                                 return;
56820                             }
56821                             var result;
56822                             try {
56823                                 result = o.reader.read(response);
56824                             }catch(e){
56825                                 Roo.log("load exception?");
56826                                 this.fireEvent("loadexception", this, o, response, e);
56827                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
56828                                 return;
56829                             }
56830                             Roo.log("ready...");        
56831                             // loop through result.records;
56832                             // and set this.tdate[date] = [] << array of records..
56833                             _this.tdata  = {};
56834                             Roo.each(result.records, function(r){
56835                                 //Roo.log(r.data);
56836                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
56837                                     _this.tdata[r.data.when_dt.format('j')] = [];
56838                                 }
56839                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
56840                             });
56841                             
56842                             //Roo.log(_this.tdata);
56843                             
56844                             result.records = [];
56845                             result.totalRecords = 6;
56846                     
56847                             // let's generate some duumy records for the rows.
56848                             //var st = _this.dateField.getValue();
56849                             
56850                             // work out monday..
56851                             //st = st.add(Date.DAY, -1 * st.format('w'));
56852                             
56853                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
56854                             
56855                             var firstOfMonth = date.getFirstDayOfMonth();
56856                             var days = date.getDaysInMonth();
56857                             var d = 1;
56858                             var firstAdded = false;
56859                             for (var i = 0; i < result.totalRecords ; i++) {
56860                                 //var d= st.add(Date.DAY, i);
56861                                 var row = {};
56862                                 var added = 0;
56863                                 for(var w = 0 ; w < 7 ; w++){
56864                                     if(!firstAdded && firstOfMonth != w){
56865                                         continue;
56866                                     }
56867                                     if(d > days){
56868                                         continue;
56869                                     }
56870                                     firstAdded = true;
56871                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
56872                                     row['weekday'+w] = String.format(
56873                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
56874                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
56875                                                     d,
56876                                                     date.format('Y-m-')+dd
56877                                                 );
56878                                     added++;
56879                                     if(typeof(_this.tdata[d]) != 'undefined'){
56880                                         Roo.each(_this.tdata[d], function(r){
56881                                             var is_sub = '';
56882                                             var deactive = '';
56883                                             var id = r.id;
56884                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
56885                                             if(r.parent_id*1>0){
56886                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
56887                                                 id = r.parent_id;
56888                                             }
56889                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
56890                                                 deactive = 'de-act-link';
56891                                             }
56892                                             
56893                                             row['weekday'+w] += String.format(
56894                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
56895                                                     id, //0
56896                                                     r.product_id_name, //1
56897                                                     r.when_dt.format('h:ia'), //2
56898                                                     is_sub, //3
56899                                                     deactive, //4
56900                                                     desc // 5
56901                                             );
56902                                         });
56903                                     }
56904                                     d++;
56905                                 }
56906                                 
56907                                 // only do this if something added..
56908                                 if(added > 0){ 
56909                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
56910                                 }
56911                                 
56912                                 
56913                                 // push it twice. (second one with an hour..
56914                                 
56915                             }
56916                             //Roo.log(result);
56917                             this.fireEvent("load", this, o, o.request.arg);
56918                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
56919                         },
56920                     sortInfo : {field: 'when_dt', direction : 'ASC' },
56921                     proxy : {
56922                         xtype: 'HttpProxy',
56923                         xns: Roo.data,
56924                         method : 'GET',
56925                         url : baseURL + '/Roo/Shop_course.php'
56926                     },
56927                     reader : {
56928                         xtype: 'JsonReader',
56929                         xns: Roo.data,
56930                         id : 'id',
56931                         fields : [
56932                             {
56933                                 'name': 'id',
56934                                 'type': 'int'
56935                             },
56936                             {
56937                                 'name': 'when_dt',
56938                                 'type': 'string'
56939                             },
56940                             {
56941                                 'name': 'end_dt',
56942                                 'type': 'string'
56943                             },
56944                             {
56945                                 'name': 'parent_id',
56946                                 'type': 'int'
56947                             },
56948                             {
56949                                 'name': 'product_id',
56950                                 'type': 'int'
56951                             },
56952                             {
56953                                 'name': 'productitem_id',
56954                                 'type': 'int'
56955                             },
56956                             {
56957                                 'name': 'guid',
56958                                 'type': 'int'
56959                             }
56960                         ]
56961                     }
56962                 },
56963                 toolbar : {
56964                     xtype: 'Toolbar',
56965                     xns: Roo,
56966                     items : [
56967                         {
56968                             xtype: 'Button',
56969                             xns: Roo.Toolbar,
56970                             listeners : {
56971                                 click : function (_self, e)
56972                                 {
56973                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
56974                                     sd.setMonth(sd.getMonth()-1);
56975                                     _this.monthField.setValue(sd.format('Y-m-d'));
56976                                     _this.grid.ds.load({});
56977                                 }
56978                             },
56979                             text : "Back"
56980                         },
56981                         {
56982                             xtype: 'Separator',
56983                             xns: Roo.Toolbar
56984                         },
56985                         {
56986                             xtype: 'MonthField',
56987                             xns: Roo.form,
56988                             listeners : {
56989                                 render : function (_self)
56990                                 {
56991                                     _this.monthField = _self;
56992                                    // _this.monthField.set  today
56993                                 },
56994                                 select : function (combo, date)
56995                                 {
56996                                     _this.grid.ds.load({});
56997                                 }
56998                             },
56999                             value : (function() { return new Date(); })()
57000                         },
57001                         {
57002                             xtype: 'Separator',
57003                             xns: Roo.Toolbar
57004                         },
57005                         {
57006                             xtype: 'TextItem',
57007                             xns: Roo.Toolbar,
57008                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57009                         },
57010                         {
57011                             xtype: 'Fill',
57012                             xns: Roo.Toolbar
57013                         },
57014                         {
57015                             xtype: 'Button',
57016                             xns: Roo.Toolbar,
57017                             listeners : {
57018                                 click : function (_self, e)
57019                                 {
57020                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57021                                     sd.setMonth(sd.getMonth()+1);
57022                                     _this.monthField.setValue(sd.format('Y-m-d'));
57023                                     _this.grid.ds.load({});
57024                                 }
57025                             },
57026                             text : "Next"
57027                         }
57028                     ]
57029                 },
57030                  
57031             }
57032         };
57033         
57034         *//*
57035  * Based on:
57036  * Ext JS Library 1.1.1
57037  * Copyright(c) 2006-2007, Ext JS, LLC.
57038  *
57039  * Originally Released Under LGPL - original licence link has changed is not relivant.
57040  *
57041  * Fork - LGPL
57042  * <script type="text/javascript">
57043  */
57044  
57045 /**
57046  * @class Roo.LoadMask
57047  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57048  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57049  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57050  * element's UpdateManager load indicator and will be destroyed after the initial load.
57051  * @constructor
57052  * Create a new LoadMask
57053  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57054  * @param {Object} config The config object
57055  */
57056 Roo.LoadMask = function(el, config){
57057     this.el = Roo.get(el);
57058     Roo.apply(this, config);
57059     if(this.store){
57060         this.store.on('beforeload', this.onBeforeLoad, this);
57061         this.store.on('load', this.onLoad, this);
57062         this.store.on('loadexception', this.onLoadException, this);
57063         this.removeMask = false;
57064     }else{
57065         var um = this.el.getUpdateManager();
57066         um.showLoadIndicator = false; // disable the default indicator
57067         um.on('beforeupdate', this.onBeforeLoad, this);
57068         um.on('update', this.onLoad, this);
57069         um.on('failure', this.onLoad, this);
57070         this.removeMask = true;
57071     }
57072 };
57073
57074 Roo.LoadMask.prototype = {
57075     /**
57076      * @cfg {Boolean} removeMask
57077      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57078      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57079      */
57080     /**
57081      * @cfg {String} msg
57082      * The text to display in a centered loading message box (defaults to 'Loading...')
57083      */
57084     msg : 'Loading...',
57085     /**
57086      * @cfg {String} msgCls
57087      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57088      */
57089     msgCls : 'x-mask-loading',
57090
57091     /**
57092      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57093      * @type Boolean
57094      */
57095     disabled: false,
57096
57097     /**
57098      * Disables the mask to prevent it from being displayed
57099      */
57100     disable : function(){
57101        this.disabled = true;
57102     },
57103
57104     /**
57105      * Enables the mask so that it can be displayed
57106      */
57107     enable : function(){
57108         this.disabled = false;
57109     },
57110     
57111     onLoadException : function()
57112     {
57113         Roo.log(arguments);
57114         
57115         if (typeof(arguments[3]) != 'undefined') {
57116             Roo.MessageBox.alert("Error loading",arguments[3]);
57117         } 
57118         /*
57119         try {
57120             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57121                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57122             }   
57123         } catch(e) {
57124             
57125         }
57126         */
57127     
57128         
57129         
57130         this.el.unmask(this.removeMask);
57131     },
57132     // private
57133     onLoad : function()
57134     {
57135         this.el.unmask(this.removeMask);
57136     },
57137
57138     // private
57139     onBeforeLoad : function(){
57140         if(!this.disabled){
57141             this.el.mask(this.msg, this.msgCls);
57142         }
57143     },
57144
57145     // private
57146     destroy : function(){
57147         if(this.store){
57148             this.store.un('beforeload', this.onBeforeLoad, this);
57149             this.store.un('load', this.onLoad, this);
57150             this.store.un('loadexception', this.onLoadException, this);
57151         }else{
57152             var um = this.el.getUpdateManager();
57153             um.un('beforeupdate', this.onBeforeLoad, this);
57154             um.un('update', this.onLoad, this);
57155             um.un('failure', this.onLoad, this);
57156         }
57157     }
57158 };/*
57159  * Based on:
57160  * Ext JS Library 1.1.1
57161  * Copyright(c) 2006-2007, Ext JS, LLC.
57162  *
57163  * Originally Released Under LGPL - original licence link has changed is not relivant.
57164  *
57165  * Fork - LGPL
57166  * <script type="text/javascript">
57167  */
57168
57169
57170 /**
57171  * @class Roo.XTemplate
57172  * @extends Roo.Template
57173  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57174 <pre><code>
57175 var t = new Roo.XTemplate(
57176         '&lt;select name="{name}"&gt;',
57177                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57178         '&lt;/select&gt;'
57179 );
57180  
57181 // then append, applying the master template values
57182  </code></pre>
57183  *
57184  * Supported features:
57185  *
57186  *  Tags:
57187
57188 <pre><code>
57189       {a_variable} - output encoded.
57190       {a_variable.format:("Y-m-d")} - call a method on the variable
57191       {a_variable:raw} - unencoded output
57192       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57193       {a_variable:this.method_on_template(...)} - call a method on the template object.
57194  
57195 </code></pre>
57196  *  The tpl tag:
57197 <pre><code>
57198         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57199         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57200         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57201         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57202   
57203         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57204         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57205 </code></pre>
57206  *      
57207  */
57208 Roo.XTemplate = function()
57209 {
57210     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57211     if (this.html) {
57212         this.compile();
57213     }
57214 };
57215
57216
57217 Roo.extend(Roo.XTemplate, Roo.Template, {
57218
57219     /**
57220      * The various sub templates
57221      */
57222     tpls : false,
57223     /**
57224      *
57225      * basic tag replacing syntax
57226      * WORD:WORD()
57227      *
57228      * // you can fake an object call by doing this
57229      *  x.t:(test,tesT) 
57230      * 
57231      */
57232     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57233
57234     /**
57235      * compile the template
57236      *
57237      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57238      *
57239      */
57240     compile: function()
57241     {
57242         var s = this.html;
57243      
57244         s = ['<tpl>', s, '</tpl>'].join('');
57245     
57246         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57247             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57248             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57249             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57250             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57251             m,
57252             id     = 0,
57253             tpls   = [];
57254     
57255         while(true == !!(m = s.match(re))){
57256             var forMatch   = m[0].match(nameRe),
57257                 ifMatch   = m[0].match(ifRe),
57258                 execMatch   = m[0].match(execRe),
57259                 namedMatch   = m[0].match(namedRe),
57260                 
57261                 exp  = null, 
57262                 fn   = null,
57263                 exec = null,
57264                 name = forMatch && forMatch[1] ? forMatch[1] : '';
57265                 
57266             if (ifMatch) {
57267                 // if - puts fn into test..
57268                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
57269                 if(exp){
57270                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
57271                 }
57272             }
57273             
57274             if (execMatch) {
57275                 // exec - calls a function... returns empty if true is  returned.
57276                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
57277                 if(exp){
57278                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
57279                 }
57280             }
57281             
57282             
57283             if (name) {
57284                 // for = 
57285                 switch(name){
57286                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
57287                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
57288                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
57289                 }
57290             }
57291             var uid = namedMatch ? namedMatch[1] : id;
57292             
57293             
57294             tpls.push({
57295                 id:     namedMatch ? namedMatch[1] : id,
57296                 target: name,
57297                 exec:   exec,
57298                 test:   fn,
57299                 body:   m[1] || ''
57300             });
57301             if (namedMatch) {
57302                 s = s.replace(m[0], '');
57303             } else { 
57304                 s = s.replace(m[0], '{xtpl'+ id + '}');
57305             }
57306             ++id;
57307         }
57308         this.tpls = [];
57309         for(var i = tpls.length-1; i >= 0; --i){
57310             this.compileTpl(tpls[i]);
57311             this.tpls[tpls[i].id] = tpls[i];
57312         }
57313         this.master = tpls[tpls.length-1];
57314         return this;
57315     },
57316     /**
57317      * same as applyTemplate, except it's done to one of the subTemplates
57318      * when using named templates, you can do:
57319      *
57320      * var str = pl.applySubTemplate('your-name', values);
57321      *
57322      * 
57323      * @param {Number} id of the template
57324      * @param {Object} values to apply to template
57325      * @param {Object} parent (normaly the instance of this object)
57326      */
57327     applySubTemplate : function(id, values, parent)
57328     {
57329         
57330         
57331         var t = this.tpls[id];
57332         
57333         
57334         try { 
57335             if(t.test && !t.test.call(this, values, parent)){
57336                 return '';
57337             }
57338         } catch(e) {
57339             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
57340             Roo.log(e.toString());
57341             Roo.log(t.test);
57342             return ''
57343         }
57344         try { 
57345             
57346             if(t.exec && t.exec.call(this, values, parent)){
57347                 return '';
57348             }
57349         } catch(e) {
57350             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
57351             Roo.log(e.toString());
57352             Roo.log(t.exec);
57353             return ''
57354         }
57355         try {
57356             var vs = t.target ? t.target.call(this, values, parent) : values;
57357             parent = t.target ? values : parent;
57358             if(t.target && vs instanceof Array){
57359                 var buf = [];
57360                 for(var i = 0, len = vs.length; i < len; i++){
57361                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
57362                 }
57363                 return buf.join('');
57364             }
57365             return t.compiled.call(this, vs, parent);
57366         } catch (e) {
57367             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
57368             Roo.log(e.toString());
57369             Roo.log(t.compiled);
57370             return '';
57371         }
57372     },
57373
57374     compileTpl : function(tpl)
57375     {
57376         var fm = Roo.util.Format;
57377         var useF = this.disableFormats !== true;
57378         var sep = Roo.isGecko ? "+" : ",";
57379         var undef = function(str) {
57380             Roo.log("Property not found :"  + str);
57381             return '';
57382         };
57383         
57384         var fn = function(m, name, format, args)
57385         {
57386             //Roo.log(arguments);
57387             args = args ? args.replace(/\\'/g,"'") : args;
57388             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
57389             if (typeof(format) == 'undefined') {
57390                 format= 'htmlEncode';
57391             }
57392             if (format == 'raw' ) {
57393                 format = false;
57394             }
57395             
57396             if(name.substr(0, 4) == 'xtpl'){
57397                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
57398             }
57399             
57400             // build an array of options to determine if value is undefined..
57401             
57402             // basically get 'xxxx.yyyy' then do
57403             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
57404             //    (function () { Roo.log("Property not found"); return ''; })() :
57405             //    ......
57406             
57407             var udef_ar = [];
57408             var lookfor = '';
57409             Roo.each(name.split('.'), function(st) {
57410                 lookfor += (lookfor.length ? '.': '') + st;
57411                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
57412             });
57413             
57414             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
57415             
57416             
57417             if(format && useF){
57418                 
57419                 args = args ? ',' + args : "";
57420                  
57421                 if(format.substr(0, 5) != "this."){
57422                     format = "fm." + format + '(';
57423                 }else{
57424                     format = 'this.call("'+ format.substr(5) + '", ';
57425                     args = ", values";
57426                 }
57427                 
57428                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
57429             }
57430              
57431             if (args.length) {
57432                 // called with xxyx.yuu:(test,test)
57433                 // change to ()
57434                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
57435             }
57436             // raw.. - :raw modifier..
57437             return "'"+ sep + udef_st  + name + ")"+sep+"'";
57438             
57439         };
57440         var body;
57441         // branched to use + in gecko and [].join() in others
57442         if(Roo.isGecko){
57443             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
57444                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
57445                     "';};};";
57446         }else{
57447             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
57448             body.push(tpl.body.replace(/(\r\n|\n)/g,
57449                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
57450             body.push("'].join('');};};");
57451             body = body.join('');
57452         }
57453         
57454         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
57455        
57456         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
57457         eval(body);
57458         
57459         return this;
57460     },
57461
57462     applyTemplate : function(values){
57463         return this.master.compiled.call(this, values, {});
57464         //var s = this.subs;
57465     },
57466
57467     apply : function(){
57468         return this.applyTemplate.apply(this, arguments);
57469     }
57470
57471  });
57472
57473 Roo.XTemplate.from = function(el){
57474     el = Roo.getDom(el);
57475     return new Roo.XTemplate(el.value || el.innerHTML);
57476 };