roojs-ui.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 || "m/d/Y");
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  * Based on:
13637  * Ext JS Library 1.1.1
13638  * Copyright(c) 2006-2007, Ext JS, LLC.
13639  *
13640  * Originally Released Under LGPL - original licence link has changed is not relivant.
13641  *
13642  * Fork - LGPL
13643  * <script type="text/javascript">
13644  */
13645
13646
13647  
13648
13649 /**
13650  * @class Roo.MasterTemplate
13651  * @extends Roo.Template
13652  * Provides a template that can have child templates. The syntax is:
13653 <pre><code>
13654 var t = new Roo.MasterTemplate(
13655         '&lt;select name="{name}"&gt;',
13656                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13657         '&lt;/select&gt;'
13658 );
13659 t.add('options', {value: 'foo', text: 'bar'});
13660 // or you can add multiple child elements in one shot
13661 t.addAll('options', [
13662     {value: 'foo', text: 'bar'},
13663     {value: 'foo2', text: 'bar2'},
13664     {value: 'foo3', text: 'bar3'}
13665 ]);
13666 // then append, applying the master template values
13667 t.append('my-form', {name: 'my-select'});
13668 </code></pre>
13669 * A name attribute for the child template is not required if you have only one child
13670 * template or you want to refer to them by index.
13671  */
13672 Roo.MasterTemplate = function(){
13673     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13674     this.originalHtml = this.html;
13675     var st = {};
13676     var m, re = this.subTemplateRe;
13677     re.lastIndex = 0;
13678     var subIndex = 0;
13679     while(m = re.exec(this.html)){
13680         var name = m[1], content = m[2];
13681         st[subIndex] = {
13682             name: name,
13683             index: subIndex,
13684             buffer: [],
13685             tpl : new Roo.Template(content)
13686         };
13687         if(name){
13688             st[name] = st[subIndex];
13689         }
13690         st[subIndex].tpl.compile();
13691         st[subIndex].tpl.call = this.call.createDelegate(this);
13692         subIndex++;
13693     }
13694     this.subCount = subIndex;
13695     this.subs = st;
13696 };
13697 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13698     /**
13699     * The regular expression used to match sub templates
13700     * @type RegExp
13701     * @property
13702     */
13703     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13704
13705     /**
13706      * Applies the passed values to a child template.
13707      * @param {String/Number} name (optional) The name or index of the child template
13708      * @param {Array/Object} values The values to be applied to the template
13709      * @return {MasterTemplate} this
13710      */
13711      add : function(name, values){
13712         if(arguments.length == 1){
13713             values = arguments[0];
13714             name = 0;
13715         }
13716         var s = this.subs[name];
13717         s.buffer[s.buffer.length] = s.tpl.apply(values);
13718         return this;
13719     },
13720
13721     /**
13722      * Applies all the passed values to a child template.
13723      * @param {String/Number} name (optional) The name or index of the child template
13724      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13725      * @param {Boolean} reset (optional) True to reset the template first
13726      * @return {MasterTemplate} this
13727      */
13728     fill : function(name, values, reset){
13729         var a = arguments;
13730         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13731             values = a[0];
13732             name = 0;
13733             reset = a[1];
13734         }
13735         if(reset){
13736             this.reset();
13737         }
13738         for(var i = 0, len = values.length; i < len; i++){
13739             this.add(name, values[i]);
13740         }
13741         return this;
13742     },
13743
13744     /**
13745      * Resets the template for reuse
13746      * @return {MasterTemplate} this
13747      */
13748      reset : function(){
13749         var s = this.subs;
13750         for(var i = 0; i < this.subCount; i++){
13751             s[i].buffer = [];
13752         }
13753         return this;
13754     },
13755
13756     applyTemplate : function(values){
13757         var s = this.subs;
13758         var replaceIndex = -1;
13759         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13760             return s[++replaceIndex].buffer.join("");
13761         });
13762         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13763     },
13764
13765     apply : function(){
13766         return this.applyTemplate.apply(this, arguments);
13767     },
13768
13769     compile : function(){return this;}
13770 });
13771
13772 /**
13773  * Alias for fill().
13774  * @method
13775  */
13776 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13777  /**
13778  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13779  * var tpl = Roo.MasterTemplate.from('element-id');
13780  * @param {String/HTMLElement} el
13781  * @param {Object} config
13782  * @static
13783  */
13784 Roo.MasterTemplate.from = function(el, config){
13785     el = Roo.getDom(el);
13786     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13787 };/*
13788  * Based on:
13789  * Ext JS Library 1.1.1
13790  * Copyright(c) 2006-2007, Ext JS, LLC.
13791  *
13792  * Originally Released Under LGPL - original licence link has changed is not relivant.
13793  *
13794  * Fork - LGPL
13795  * <script type="text/javascript">
13796  */
13797
13798  
13799 /**
13800  * @class Roo.util.CSS
13801  * Utility class for manipulating CSS rules
13802  * @singleton
13803  */
13804 Roo.util.CSS = function(){
13805         var rules = null;
13806         var doc = document;
13807
13808     var camelRe = /(-[a-z])/gi;
13809     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13810
13811    return {
13812    /**
13813     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13814     * tag and appended to the HEAD of the document.
13815     * @param {String|Object} cssText The text containing the css rules
13816     * @param {String} id An id to add to the stylesheet for later removal
13817     * @return {StyleSheet}
13818     */
13819     createStyleSheet : function(cssText, id){
13820         var ss;
13821         var head = doc.getElementsByTagName("head")[0];
13822         var nrules = doc.createElement("style");
13823         nrules.setAttribute("type", "text/css");
13824         if(id){
13825             nrules.setAttribute("id", id);
13826         }
13827         if (typeof(cssText) != 'string') {
13828             // support object maps..
13829             // not sure if this a good idea.. 
13830             // perhaps it should be merged with the general css handling
13831             // and handle js style props.
13832             var cssTextNew = [];
13833             for(var n in cssText) {
13834                 var citems = [];
13835                 for(var k in cssText[n]) {
13836                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13837                 }
13838                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13839                 
13840             }
13841             cssText = cssTextNew.join("\n");
13842             
13843         }
13844        
13845        
13846        if(Roo.isIE){
13847            head.appendChild(nrules);
13848            ss = nrules.styleSheet;
13849            ss.cssText = cssText;
13850        }else{
13851            try{
13852                 nrules.appendChild(doc.createTextNode(cssText));
13853            }catch(e){
13854                nrules.cssText = cssText; 
13855            }
13856            head.appendChild(nrules);
13857            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13858        }
13859        this.cacheStyleSheet(ss);
13860        return ss;
13861    },
13862
13863    /**
13864     * Removes a style or link tag by id
13865     * @param {String} id The id of the tag
13866     */
13867    removeStyleSheet : function(id){
13868        var existing = doc.getElementById(id);
13869        if(existing){
13870            existing.parentNode.removeChild(existing);
13871        }
13872    },
13873
13874    /**
13875     * Dynamically swaps an existing stylesheet reference for a new one
13876     * @param {String} id The id of an existing link tag to remove
13877     * @param {String} url The href of the new stylesheet to include
13878     */
13879    swapStyleSheet : function(id, url){
13880        this.removeStyleSheet(id);
13881        var ss = doc.createElement("link");
13882        ss.setAttribute("rel", "stylesheet");
13883        ss.setAttribute("type", "text/css");
13884        ss.setAttribute("id", id);
13885        ss.setAttribute("href", url);
13886        doc.getElementsByTagName("head")[0].appendChild(ss);
13887    },
13888    
13889    /**
13890     * Refresh the rule cache if you have dynamically added stylesheets
13891     * @return {Object} An object (hash) of rules indexed by selector
13892     */
13893    refreshCache : function(){
13894        return this.getRules(true);
13895    },
13896
13897    // private
13898    cacheStyleSheet : function(stylesheet){
13899        if(!rules){
13900            rules = {};
13901        }
13902        try{// try catch for cross domain access issue
13903            var ssRules = stylesheet.cssRules || stylesheet.rules;
13904            for(var j = ssRules.length-1; j >= 0; --j){
13905                rules[ssRules[j].selectorText] = ssRules[j];
13906            }
13907        }catch(e){}
13908    },
13909    
13910    /**
13911     * Gets all css rules for the document
13912     * @param {Boolean} refreshCache true to refresh the internal cache
13913     * @return {Object} An object (hash) of rules indexed by selector
13914     */
13915    getRules : function(refreshCache){
13916                 if(rules == null || refreshCache){
13917                         rules = {};
13918                         var ds = doc.styleSheets;
13919                         for(var i =0, len = ds.length; i < len; i++){
13920                             try{
13921                         this.cacheStyleSheet(ds[i]);
13922                     }catch(e){} 
13923                 }
13924                 }
13925                 return rules;
13926         },
13927         
13928         /**
13929     * Gets an an individual CSS rule by selector(s)
13930     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13931     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13932     * @return {CSSRule} The CSS rule or null if one is not found
13933     */
13934    getRule : function(selector, refreshCache){
13935                 var rs = this.getRules(refreshCache);
13936                 if(!(selector instanceof Array)){
13937                     return rs[selector];
13938                 }
13939                 for(var i = 0; i < selector.length; i++){
13940                         if(rs[selector[i]]){
13941                                 return rs[selector[i]];
13942                         }
13943                 }
13944                 return null;
13945         },
13946         
13947         
13948         /**
13949     * Updates a rule property
13950     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13951     * @param {String} property The css property
13952     * @param {String} value The new value for the property
13953     * @return {Boolean} true If a rule was found and updated
13954     */
13955    updateRule : function(selector, property, value){
13956                 if(!(selector instanceof Array)){
13957                         var rule = this.getRule(selector);
13958                         if(rule){
13959                                 rule.style[property.replace(camelRe, camelFn)] = value;
13960                                 return true;
13961                         }
13962                 }else{
13963                         for(var i = 0; i < selector.length; i++){
13964                                 if(this.updateRule(selector[i], property, value)){
13965                                         return true;
13966                                 }
13967                         }
13968                 }
13969                 return false;
13970         }
13971    };   
13972 }();/*
13973  * Based on:
13974  * Ext JS Library 1.1.1
13975  * Copyright(c) 2006-2007, Ext JS, LLC.
13976  *
13977  * Originally Released Under LGPL - original licence link has changed is not relivant.
13978  *
13979  * Fork - LGPL
13980  * <script type="text/javascript">
13981  */
13982
13983  
13984
13985 /**
13986  * @class Roo.util.ClickRepeater
13987  * @extends Roo.util.Observable
13988  * 
13989  * A wrapper class which can be applied to any element. Fires a "click" event while the
13990  * mouse is pressed. The interval between firings may be specified in the config but
13991  * defaults to 10 milliseconds.
13992  * 
13993  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13994  * 
13995  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13996  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13997  * Similar to an autorepeat key delay.
13998  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13999  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14000  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14001  *           "interval" and "delay" are ignored. "immediate" is honored.
14002  * @cfg {Boolean} preventDefault True to prevent the default click event
14003  * @cfg {Boolean} stopDefault True to stop the default click event
14004  * 
14005  * @history
14006  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14007  *     2007-02-02 jvs Renamed to ClickRepeater
14008  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14009  *
14010  *  @constructor
14011  * @param {String/HTMLElement/Element} el The element to listen on
14012  * @param {Object} config
14013  **/
14014 Roo.util.ClickRepeater = function(el, config)
14015 {
14016     this.el = Roo.get(el);
14017     this.el.unselectable();
14018
14019     Roo.apply(this, config);
14020
14021     this.addEvents({
14022     /**
14023      * @event mousedown
14024      * Fires when the mouse button is depressed.
14025      * @param {Roo.util.ClickRepeater} this
14026      */
14027         "mousedown" : true,
14028     /**
14029      * @event click
14030      * Fires on a specified interval during the time the element is pressed.
14031      * @param {Roo.util.ClickRepeater} this
14032      */
14033         "click" : true,
14034     /**
14035      * @event mouseup
14036      * Fires when the mouse key is released.
14037      * @param {Roo.util.ClickRepeater} this
14038      */
14039         "mouseup" : true
14040     });
14041
14042     this.el.on("mousedown", this.handleMouseDown, this);
14043     if(this.preventDefault || this.stopDefault){
14044         this.el.on("click", function(e){
14045             if(this.preventDefault){
14046                 e.preventDefault();
14047             }
14048             if(this.stopDefault){
14049                 e.stopEvent();
14050             }
14051         }, this);
14052     }
14053
14054     // allow inline handler
14055     if(this.handler){
14056         this.on("click", this.handler,  this.scope || this);
14057     }
14058
14059     Roo.util.ClickRepeater.superclass.constructor.call(this);
14060 };
14061
14062 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14063     interval : 20,
14064     delay: 250,
14065     preventDefault : true,
14066     stopDefault : false,
14067     timer : 0,
14068
14069     // private
14070     handleMouseDown : function(){
14071         clearTimeout(this.timer);
14072         this.el.blur();
14073         if(this.pressClass){
14074             this.el.addClass(this.pressClass);
14075         }
14076         this.mousedownTime = new Date();
14077
14078         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14079         this.el.on("mouseout", this.handleMouseOut, this);
14080
14081         this.fireEvent("mousedown", this);
14082         this.fireEvent("click", this);
14083         
14084         this.timer = this.click.defer(this.delay || this.interval, this);
14085     },
14086
14087     // private
14088     click : function(){
14089         this.fireEvent("click", this);
14090         this.timer = this.click.defer(this.getInterval(), this);
14091     },
14092
14093     // private
14094     getInterval: function(){
14095         if(!this.accelerate){
14096             return this.interval;
14097         }
14098         var pressTime = this.mousedownTime.getElapsed();
14099         if(pressTime < 500){
14100             return 400;
14101         }else if(pressTime < 1700){
14102             return 320;
14103         }else if(pressTime < 2600){
14104             return 250;
14105         }else if(pressTime < 3500){
14106             return 180;
14107         }else if(pressTime < 4400){
14108             return 140;
14109         }else if(pressTime < 5300){
14110             return 80;
14111         }else if(pressTime < 6200){
14112             return 50;
14113         }else{
14114             return 10;
14115         }
14116     },
14117
14118     // private
14119     handleMouseOut : function(){
14120         clearTimeout(this.timer);
14121         if(this.pressClass){
14122             this.el.removeClass(this.pressClass);
14123         }
14124         this.el.on("mouseover", this.handleMouseReturn, this);
14125     },
14126
14127     // private
14128     handleMouseReturn : function(){
14129         this.el.un("mouseover", this.handleMouseReturn);
14130         if(this.pressClass){
14131             this.el.addClass(this.pressClass);
14132         }
14133         this.click();
14134     },
14135
14136     // private
14137     handleMouseUp : function(){
14138         clearTimeout(this.timer);
14139         this.el.un("mouseover", this.handleMouseReturn);
14140         this.el.un("mouseout", this.handleMouseOut);
14141         Roo.get(document).un("mouseup", this.handleMouseUp);
14142         this.el.removeClass(this.pressClass);
14143         this.fireEvent("mouseup", this);
14144     }
14145 });/*
14146  * Based on:
14147  * Ext JS Library 1.1.1
14148  * Copyright(c) 2006-2007, Ext JS, LLC.
14149  *
14150  * Originally Released Under LGPL - original licence link has changed is not relivant.
14151  *
14152  * Fork - LGPL
14153  * <script type="text/javascript">
14154  */
14155
14156  
14157 /**
14158  * @class Roo.KeyNav
14159  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14160  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14161  * way to implement custom navigation schemes for any UI component.</p>
14162  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14163  * pageUp, pageDown, del, home, end.  Usage:</p>
14164  <pre><code>
14165 var nav = new Roo.KeyNav("my-element", {
14166     "left" : function(e){
14167         this.moveLeft(e.ctrlKey);
14168     },
14169     "right" : function(e){
14170         this.moveRight(e.ctrlKey);
14171     },
14172     "enter" : function(e){
14173         this.save();
14174     },
14175     scope : this
14176 });
14177 </code></pre>
14178  * @constructor
14179  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14180  * @param {Object} config The config
14181  */
14182 Roo.KeyNav = function(el, config){
14183     this.el = Roo.get(el);
14184     Roo.apply(this, config);
14185     if(!this.disabled){
14186         this.disabled = true;
14187         this.enable();
14188     }
14189 };
14190
14191 Roo.KeyNav.prototype = {
14192     /**
14193      * @cfg {Boolean} disabled
14194      * True to disable this KeyNav instance (defaults to false)
14195      */
14196     disabled : false,
14197     /**
14198      * @cfg {String} defaultEventAction
14199      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14200      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14201      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14202      */
14203     defaultEventAction: "stopEvent",
14204     /**
14205      * @cfg {Boolean} forceKeyDown
14206      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14207      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14208      * handle keydown instead of keypress.
14209      */
14210     forceKeyDown : false,
14211
14212     // private
14213     prepareEvent : function(e){
14214         var k = e.getKey();
14215         var h = this.keyToHandler[k];
14216         //if(h && this[h]){
14217         //    e.stopPropagation();
14218         //}
14219         if(Roo.isSafari && h && k >= 37 && k <= 40){
14220             e.stopEvent();
14221         }
14222     },
14223
14224     // private
14225     relay : function(e){
14226         var k = e.getKey();
14227         var h = this.keyToHandler[k];
14228         if(h && this[h]){
14229             if(this.doRelay(e, this[h], h) !== true){
14230                 e[this.defaultEventAction]();
14231             }
14232         }
14233     },
14234
14235     // private
14236     doRelay : function(e, h, hname){
14237         return h.call(this.scope || this, e);
14238     },
14239
14240     // possible handlers
14241     enter : false,
14242     left : false,
14243     right : false,
14244     up : false,
14245     down : false,
14246     tab : false,
14247     esc : false,
14248     pageUp : false,
14249     pageDown : false,
14250     del : false,
14251     home : false,
14252     end : false,
14253
14254     // quick lookup hash
14255     keyToHandler : {
14256         37 : "left",
14257         39 : "right",
14258         38 : "up",
14259         40 : "down",
14260         33 : "pageUp",
14261         34 : "pageDown",
14262         46 : "del",
14263         36 : "home",
14264         35 : "end",
14265         13 : "enter",
14266         27 : "esc",
14267         9  : "tab"
14268     },
14269
14270         /**
14271          * Enable this KeyNav
14272          */
14273         enable: function(){
14274                 if(this.disabled){
14275             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14276             // the EventObject will normalize Safari automatically
14277             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14278                 this.el.on("keydown", this.relay,  this);
14279             }else{
14280                 this.el.on("keydown", this.prepareEvent,  this);
14281                 this.el.on("keypress", this.relay,  this);
14282             }
14283                     this.disabled = false;
14284                 }
14285         },
14286
14287         /**
14288          * Disable this KeyNav
14289          */
14290         disable: function(){
14291                 if(!this.disabled){
14292                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14293                 this.el.un("keydown", this.relay);
14294             }else{
14295                 this.el.un("keydown", this.prepareEvent);
14296                 this.el.un("keypress", this.relay);
14297             }
14298                     this.disabled = true;
14299                 }
14300         }
14301 };/*
14302  * Based on:
14303  * Ext JS Library 1.1.1
14304  * Copyright(c) 2006-2007, Ext JS, LLC.
14305  *
14306  * Originally Released Under LGPL - original licence link has changed is not relivant.
14307  *
14308  * Fork - LGPL
14309  * <script type="text/javascript">
14310  */
14311
14312  
14313 /**
14314  * @class Roo.KeyMap
14315  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14316  * The constructor accepts the same config object as defined by {@link #addBinding}.
14317  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14318  * combination it will call the function with this signature (if the match is a multi-key
14319  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14320  * A KeyMap can also handle a string representation of keys.<br />
14321  * Usage:
14322  <pre><code>
14323 // map one key by key code
14324 var map = new Roo.KeyMap("my-element", {
14325     key: 13, // or Roo.EventObject.ENTER
14326     fn: myHandler,
14327     scope: myObject
14328 });
14329
14330 // map multiple keys to one action by string
14331 var map = new Roo.KeyMap("my-element", {
14332     key: "a\r\n\t",
14333     fn: myHandler,
14334     scope: myObject
14335 });
14336
14337 // map multiple keys to multiple actions by strings and array of codes
14338 var map = new Roo.KeyMap("my-element", [
14339     {
14340         key: [10,13],
14341         fn: function(){ alert("Return was pressed"); }
14342     }, {
14343         key: "abc",
14344         fn: function(){ alert('a, b or c was pressed'); }
14345     }, {
14346         key: "\t",
14347         ctrl:true,
14348         shift:true,
14349         fn: function(){ alert('Control + shift + tab was pressed.'); }
14350     }
14351 ]);
14352 </code></pre>
14353  * <b>Note: A KeyMap starts enabled</b>
14354  * @constructor
14355  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14356  * @param {Object} config The config (see {@link #addBinding})
14357  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14358  */
14359 Roo.KeyMap = function(el, config, eventName){
14360     this.el  = Roo.get(el);
14361     this.eventName = eventName || "keydown";
14362     this.bindings = [];
14363     if(config){
14364         this.addBinding(config);
14365     }
14366     this.enable();
14367 };
14368
14369 Roo.KeyMap.prototype = {
14370     /**
14371      * True to stop the event from bubbling and prevent the default browser action if the
14372      * key was handled by the KeyMap (defaults to false)
14373      * @type Boolean
14374      */
14375     stopEvent : false,
14376
14377     /**
14378      * Add a new binding to this KeyMap. The following config object properties are supported:
14379      * <pre>
14380 Property    Type             Description
14381 ----------  ---------------  ----------------------------------------------------------------------
14382 key         String/Array     A single keycode or an array of keycodes to handle
14383 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14384 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14385 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14386 fn          Function         The function to call when KeyMap finds the expected key combination
14387 scope       Object           The scope of the callback function
14388 </pre>
14389      *
14390      * Usage:
14391      * <pre><code>
14392 // Create a KeyMap
14393 var map = new Roo.KeyMap(document, {
14394     key: Roo.EventObject.ENTER,
14395     fn: handleKey,
14396     scope: this
14397 });
14398
14399 //Add a new binding to the existing KeyMap later
14400 map.addBinding({
14401     key: 'abc',
14402     shift: true,
14403     fn: handleKey,
14404     scope: this
14405 });
14406 </code></pre>
14407      * @param {Object/Array} config A single KeyMap config or an array of configs
14408      */
14409         addBinding : function(config){
14410         if(config instanceof Array){
14411             for(var i = 0, len = config.length; i < len; i++){
14412                 this.addBinding(config[i]);
14413             }
14414             return;
14415         }
14416         var keyCode = config.key,
14417             shift = config.shift, 
14418             ctrl = config.ctrl, 
14419             alt = config.alt,
14420             fn = config.fn,
14421             scope = config.scope;
14422         if(typeof keyCode == "string"){
14423             var ks = [];
14424             var keyString = keyCode.toUpperCase();
14425             for(var j = 0, len = keyString.length; j < len; j++){
14426                 ks.push(keyString.charCodeAt(j));
14427             }
14428             keyCode = ks;
14429         }
14430         var keyArray = keyCode instanceof Array;
14431         var handler = function(e){
14432             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14433                 var k = e.getKey();
14434                 if(keyArray){
14435                     for(var i = 0, len = keyCode.length; i < len; i++){
14436                         if(keyCode[i] == k){
14437                           if(this.stopEvent){
14438                               e.stopEvent();
14439                           }
14440                           fn.call(scope || window, k, e);
14441                           return;
14442                         }
14443                     }
14444                 }else{
14445                     if(k == keyCode){
14446                         if(this.stopEvent){
14447                            e.stopEvent();
14448                         }
14449                         fn.call(scope || window, k, e);
14450                     }
14451                 }
14452             }
14453         };
14454         this.bindings.push(handler);  
14455         },
14456
14457     /**
14458      * Shorthand for adding a single key listener
14459      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14460      * following options:
14461      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14462      * @param {Function} fn The function to call
14463      * @param {Object} scope (optional) The scope of the function
14464      */
14465     on : function(key, fn, scope){
14466         var keyCode, shift, ctrl, alt;
14467         if(typeof key == "object" && !(key instanceof Array)){
14468             keyCode = key.key;
14469             shift = key.shift;
14470             ctrl = key.ctrl;
14471             alt = key.alt;
14472         }else{
14473             keyCode = key;
14474         }
14475         this.addBinding({
14476             key: keyCode,
14477             shift: shift,
14478             ctrl: ctrl,
14479             alt: alt,
14480             fn: fn,
14481             scope: scope
14482         })
14483     },
14484
14485     // private
14486     handleKeyDown : function(e){
14487             if(this.enabled){ //just in case
14488             var b = this.bindings;
14489             for(var i = 0, len = b.length; i < len; i++){
14490                 b[i].call(this, e);
14491             }
14492             }
14493         },
14494         
14495         /**
14496          * Returns true if this KeyMap is enabled
14497          * @return {Boolean} 
14498          */
14499         isEnabled : function(){
14500             return this.enabled;  
14501         },
14502         
14503         /**
14504          * Enables this KeyMap
14505          */
14506         enable: function(){
14507                 if(!this.enabled){
14508                     this.el.on(this.eventName, this.handleKeyDown, this);
14509                     this.enabled = true;
14510                 }
14511         },
14512
14513         /**
14514          * Disable this KeyMap
14515          */
14516         disable: function(){
14517                 if(this.enabled){
14518                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14519                     this.enabled = false;
14520                 }
14521         }
14522 };/*
14523  * Based on:
14524  * Ext JS Library 1.1.1
14525  * Copyright(c) 2006-2007, Ext JS, LLC.
14526  *
14527  * Originally Released Under LGPL - original licence link has changed is not relivant.
14528  *
14529  * Fork - LGPL
14530  * <script type="text/javascript">
14531  */
14532
14533  
14534 /**
14535  * @class Roo.util.TextMetrics
14536  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14537  * wide, in pixels, a given block of text will be.
14538  * @singleton
14539  */
14540 Roo.util.TextMetrics = function(){
14541     var shared;
14542     return {
14543         /**
14544          * Measures the size of the specified text
14545          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14546          * that can affect the size of the rendered text
14547          * @param {String} text The text to measure
14548          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14549          * in order to accurately measure the text height
14550          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14551          */
14552         measure : function(el, text, fixedWidth){
14553             if(!shared){
14554                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14555             }
14556             shared.bind(el);
14557             shared.setFixedWidth(fixedWidth || 'auto');
14558             return shared.getSize(text);
14559         },
14560
14561         /**
14562          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14563          * the overhead of multiple calls to initialize the style properties on each measurement.
14564          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14565          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14566          * in order to accurately measure the text height
14567          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14568          */
14569         createInstance : function(el, fixedWidth){
14570             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14571         }
14572     };
14573 }();
14574
14575  
14576
14577 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14578     var ml = new Roo.Element(document.createElement('div'));
14579     document.body.appendChild(ml.dom);
14580     ml.position('absolute');
14581     ml.setLeftTop(-1000, -1000);
14582     ml.hide();
14583
14584     if(fixedWidth){
14585         ml.setWidth(fixedWidth);
14586     }
14587      
14588     var instance = {
14589         /**
14590          * Returns the size of the specified text based on the internal element's style and width properties
14591          * @memberOf Roo.util.TextMetrics.Instance#
14592          * @param {String} text The text to measure
14593          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14594          */
14595         getSize : function(text){
14596             ml.update(text);
14597             var s = ml.getSize();
14598             ml.update('');
14599             return s;
14600         },
14601
14602         /**
14603          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14604          * that can affect the size of the rendered text
14605          * @memberOf Roo.util.TextMetrics.Instance#
14606          * @param {String/HTMLElement} el The element, dom node or id
14607          */
14608         bind : function(el){
14609             ml.setStyle(
14610                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14611             );
14612         },
14613
14614         /**
14615          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14616          * to set a fixed width in order to accurately measure the text height.
14617          * @memberOf Roo.util.TextMetrics.Instance#
14618          * @param {Number} width The width to set on the element
14619          */
14620         setFixedWidth : function(width){
14621             ml.setWidth(width);
14622         },
14623
14624         /**
14625          * Returns the measured width of the specified text
14626          * @memberOf Roo.util.TextMetrics.Instance#
14627          * @param {String} text The text to measure
14628          * @return {Number} width The width in pixels
14629          */
14630         getWidth : function(text){
14631             ml.dom.style.width = 'auto';
14632             return this.getSize(text).width;
14633         },
14634
14635         /**
14636          * Returns the measured height of the specified text.  For multiline text, be sure to call
14637          * {@link #setFixedWidth} if necessary.
14638          * @memberOf Roo.util.TextMetrics.Instance#
14639          * @param {String} text The text to measure
14640          * @return {Number} height The height in pixels
14641          */
14642         getHeight : function(text){
14643             return this.getSize(text).height;
14644         }
14645     };
14646
14647     instance.bind(bindTo);
14648
14649     return instance;
14650 };
14651
14652 // backwards compat
14653 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14654  * Based on:
14655  * Ext JS Library 1.1.1
14656  * Copyright(c) 2006-2007, Ext JS, LLC.
14657  *
14658  * Originally Released Under LGPL - original licence link has changed is not relivant.
14659  *
14660  * Fork - LGPL
14661  * <script type="text/javascript">
14662  */
14663
14664 /**
14665  * @class Roo.state.Provider
14666  * Abstract base class for state provider implementations. This class provides methods
14667  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14668  * Provider interface.
14669  */
14670 Roo.state.Provider = function(){
14671     /**
14672      * @event statechange
14673      * Fires when a state change occurs.
14674      * @param {Provider} this This state provider
14675      * @param {String} key The state key which was changed
14676      * @param {String} value The encoded value for the state
14677      */
14678     this.addEvents({
14679         "statechange": true
14680     });
14681     this.state = {};
14682     Roo.state.Provider.superclass.constructor.call(this);
14683 };
14684 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14685     /**
14686      * Returns the current value for a key
14687      * @param {String} name The key name
14688      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14689      * @return {Mixed} The state data
14690      */
14691     get : function(name, defaultValue){
14692         return typeof this.state[name] == "undefined" ?
14693             defaultValue : this.state[name];
14694     },
14695     
14696     /**
14697      * Clears a value from the state
14698      * @param {String} name The key name
14699      */
14700     clear : function(name){
14701         delete this.state[name];
14702         this.fireEvent("statechange", this, name, null);
14703     },
14704     
14705     /**
14706      * Sets the value for a key
14707      * @param {String} name The key name
14708      * @param {Mixed} value The value to set
14709      */
14710     set : function(name, value){
14711         this.state[name] = value;
14712         this.fireEvent("statechange", this, name, value);
14713     },
14714     
14715     /**
14716      * Decodes a string previously encoded with {@link #encodeValue}.
14717      * @param {String} value The value to decode
14718      * @return {Mixed} The decoded value
14719      */
14720     decodeValue : function(cookie){
14721         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14722         var matches = re.exec(unescape(cookie));
14723         if(!matches || !matches[1]) return; // non state cookie
14724         var type = matches[1];
14725         var v = matches[2];
14726         switch(type){
14727             case "n":
14728                 return parseFloat(v);
14729             case "d":
14730                 return new Date(Date.parse(v));
14731             case "b":
14732                 return (v == "1");
14733             case "a":
14734                 var all = [];
14735                 var values = v.split("^");
14736                 for(var i = 0, len = values.length; i < len; i++){
14737                     all.push(this.decodeValue(values[i]));
14738                 }
14739                 return all;
14740            case "o":
14741                 var all = {};
14742                 var values = v.split("^");
14743                 for(var i = 0, len = values.length; i < len; i++){
14744                     var kv = values[i].split("=");
14745                     all[kv[0]] = this.decodeValue(kv[1]);
14746                 }
14747                 return all;
14748            default:
14749                 return v;
14750         }
14751     },
14752     
14753     /**
14754      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14755      * @param {Mixed} value The value to encode
14756      * @return {String} The encoded value
14757      */
14758     encodeValue : function(v){
14759         var enc;
14760         if(typeof v == "number"){
14761             enc = "n:" + v;
14762         }else if(typeof v == "boolean"){
14763             enc = "b:" + (v ? "1" : "0");
14764         }else if(v instanceof Date){
14765             enc = "d:" + v.toGMTString();
14766         }else if(v instanceof Array){
14767             var flat = "";
14768             for(var i = 0, len = v.length; i < len; i++){
14769                 flat += this.encodeValue(v[i]);
14770                 if(i != len-1) flat += "^";
14771             }
14772             enc = "a:" + flat;
14773         }else if(typeof v == "object"){
14774             var flat = "";
14775             for(var key in v){
14776                 if(typeof v[key] != "function"){
14777                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14778                 }
14779             }
14780             enc = "o:" + flat.substring(0, flat.length-1);
14781         }else{
14782             enc = "s:" + v;
14783         }
14784         return escape(enc);        
14785     }
14786 });
14787
14788 /*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798 /**
14799  * @class Roo.state.Manager
14800  * This is the global state manager. By default all components that are "state aware" check this class
14801  * for state information if you don't pass them a custom state provider. In order for this class
14802  * to be useful, it must be initialized with a provider when your application initializes.
14803  <pre><code>
14804 // in your initialization function
14805 init : function(){
14806    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14807    ...
14808    // supposed you have a {@link Roo.BorderLayout}
14809    var layout = new Roo.BorderLayout(...);
14810    layout.restoreState();
14811    // or a {Roo.BasicDialog}
14812    var dialog = new Roo.BasicDialog(...);
14813    dialog.restoreState();
14814  </code></pre>
14815  * @singleton
14816  */
14817 Roo.state.Manager = function(){
14818     var provider = new Roo.state.Provider();
14819     
14820     return {
14821         /**
14822          * Configures the default state provider for your application
14823          * @param {Provider} stateProvider The state provider to set
14824          */
14825         setProvider : function(stateProvider){
14826             provider = stateProvider;
14827         },
14828         
14829         /**
14830          * Returns the current value for a key
14831          * @param {String} name The key name
14832          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14833          * @return {Mixed} The state data
14834          */
14835         get : function(key, defaultValue){
14836             return provider.get(key, defaultValue);
14837         },
14838         
14839         /**
14840          * Sets the value for a key
14841          * @param {String} name The key name
14842          * @param {Mixed} value The state data
14843          */
14844          set : function(key, value){
14845             provider.set(key, value);
14846         },
14847         
14848         /**
14849          * Clears a value from the state
14850          * @param {String} name The key name
14851          */
14852         clear : function(key){
14853             provider.clear(key);
14854         },
14855         
14856         /**
14857          * Gets the currently configured state provider
14858          * @return {Provider} The state provider
14859          */
14860         getProvider : function(){
14861             return provider;
14862         }
14863     };
14864 }();
14865 /*
14866  * Based on:
14867  * Ext JS Library 1.1.1
14868  * Copyright(c) 2006-2007, Ext JS, LLC.
14869  *
14870  * Originally Released Under LGPL - original licence link has changed is not relivant.
14871  *
14872  * Fork - LGPL
14873  * <script type="text/javascript">
14874  */
14875 /**
14876  * @class Roo.state.CookieProvider
14877  * @extends Roo.state.Provider
14878  * The default Provider implementation which saves state via cookies.
14879  * <br />Usage:
14880  <pre><code>
14881    var cp = new Roo.state.CookieProvider({
14882        path: "/cgi-bin/",
14883        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14884        domain: "roojs.com"
14885    })
14886    Roo.state.Manager.setProvider(cp);
14887  </code></pre>
14888  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14889  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14890  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14891  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14892  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14893  * domain the page is running on including the 'www' like 'www.roojs.com')
14894  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14895  * @constructor
14896  * Create a new CookieProvider
14897  * @param {Object} config The configuration object
14898  */
14899 Roo.state.CookieProvider = function(config){
14900     Roo.state.CookieProvider.superclass.constructor.call(this);
14901     this.path = "/";
14902     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14903     this.domain = null;
14904     this.secure = false;
14905     Roo.apply(this, config);
14906     this.state = this.readCookies();
14907 };
14908
14909 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14910     // private
14911     set : function(name, value){
14912         if(typeof value == "undefined" || value === null){
14913             this.clear(name);
14914             return;
14915         }
14916         this.setCookie(name, value);
14917         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14918     },
14919
14920     // private
14921     clear : function(name){
14922         this.clearCookie(name);
14923         Roo.state.CookieProvider.superclass.clear.call(this, name);
14924     },
14925
14926     // private
14927     readCookies : function(){
14928         var cookies = {};
14929         var c = document.cookie + ";";
14930         var re = /\s?(.*?)=(.*?);/g;
14931         var matches;
14932         while((matches = re.exec(c)) != null){
14933             var name = matches[1];
14934             var value = matches[2];
14935             if(name && name.substring(0,3) == "ys-"){
14936                 cookies[name.substr(3)] = this.decodeValue(value);
14937             }
14938         }
14939         return cookies;
14940     },
14941
14942     // private
14943     setCookie : function(name, value){
14944         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14945            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14946            ((this.path == null) ? "" : ("; path=" + this.path)) +
14947            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14948            ((this.secure == true) ? "; secure" : "");
14949     },
14950
14951     // private
14952     clearCookie : function(name){
14953         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14954            ((this.path == null) ? "" : ("; path=" + this.path)) +
14955            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14956            ((this.secure == true) ? "; secure" : "");
14957     }
14958 });/*
14959  * Based on:
14960  * Ext JS Library 1.1.1
14961  * Copyright(c) 2006-2007, Ext JS, LLC.
14962  *
14963  * Originally Released Under LGPL - original licence link has changed is not relivant.
14964  *
14965  * Fork - LGPL
14966  * <script type="text/javascript">
14967  */
14968  
14969
14970 /**
14971  * @class Roo.ComponentMgr
14972  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14973  * @singleton
14974  */
14975 Roo.ComponentMgr = function(){
14976     var all = new Roo.util.MixedCollection();
14977
14978     return {
14979         /**
14980          * Registers a component.
14981          * @param {Roo.Component} c The component
14982          */
14983         register : function(c){
14984             all.add(c);
14985         },
14986
14987         /**
14988          * Unregisters a component.
14989          * @param {Roo.Component} c The component
14990          */
14991         unregister : function(c){
14992             all.remove(c);
14993         },
14994
14995         /**
14996          * Returns a component by id
14997          * @param {String} id The component id
14998          */
14999         get : function(id){
15000             return all.get(id);
15001         },
15002
15003         /**
15004          * Registers a function that will be called when a specified component is added to ComponentMgr
15005          * @param {String} id The component id
15006          * @param {Funtction} fn The callback function
15007          * @param {Object} scope The scope of the callback
15008          */
15009         onAvailable : function(id, fn, scope){
15010             all.on("add", function(index, o){
15011                 if(o.id == id){
15012                     fn.call(scope || o, o);
15013                     all.un("add", fn, scope);
15014                 }
15015             });
15016         }
15017     };
15018 }();/*
15019  * Based on:
15020  * Ext JS Library 1.1.1
15021  * Copyright(c) 2006-2007, Ext JS, LLC.
15022  *
15023  * Originally Released Under LGPL - original licence link has changed is not relivant.
15024  *
15025  * Fork - LGPL
15026  * <script type="text/javascript">
15027  */
15028  
15029 /**
15030  * @class Roo.Component
15031  * @extends Roo.util.Observable
15032  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15033  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15034  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15035  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15036  * All visual components (widgets) that require rendering into a layout should subclass Component.
15037  * @constructor
15038  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15039  * 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
15040  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15041  */
15042 Roo.Component = function(config){
15043     config = config || {};
15044     if(config.tagName || config.dom || typeof config == "string"){ // element object
15045         config = {el: config, id: config.id || config};
15046     }
15047     this.initialConfig = config;
15048
15049     Roo.apply(this, config);
15050     this.addEvents({
15051         /**
15052          * @event disable
15053          * Fires after the component is disabled.
15054              * @param {Roo.Component} this
15055              */
15056         disable : true,
15057         /**
15058          * @event enable
15059          * Fires after the component is enabled.
15060              * @param {Roo.Component} this
15061              */
15062         enable : true,
15063         /**
15064          * @event beforeshow
15065          * Fires before the component is shown.  Return false to stop the show.
15066              * @param {Roo.Component} this
15067              */
15068         beforeshow : true,
15069         /**
15070          * @event show
15071          * Fires after the component is shown.
15072              * @param {Roo.Component} this
15073              */
15074         show : true,
15075         /**
15076          * @event beforehide
15077          * Fires before the component is hidden. Return false to stop the hide.
15078              * @param {Roo.Component} this
15079              */
15080         beforehide : true,
15081         /**
15082          * @event hide
15083          * Fires after the component is hidden.
15084              * @param {Roo.Component} this
15085              */
15086         hide : true,
15087         /**
15088          * @event beforerender
15089          * Fires before the component is rendered. Return false to stop the render.
15090              * @param {Roo.Component} this
15091              */
15092         beforerender : true,
15093         /**
15094          * @event render
15095          * Fires after the component is rendered.
15096              * @param {Roo.Component} this
15097              */
15098         render : true,
15099         /**
15100          * @event beforedestroy
15101          * Fires before the component is destroyed. Return false to stop the destroy.
15102              * @param {Roo.Component} this
15103              */
15104         beforedestroy : true,
15105         /**
15106          * @event destroy
15107          * Fires after the component is destroyed.
15108              * @param {Roo.Component} this
15109              */
15110         destroy : true
15111     });
15112     if(!this.id){
15113         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15114     }
15115     Roo.ComponentMgr.register(this);
15116     Roo.Component.superclass.constructor.call(this);
15117     this.initComponent();
15118     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15119         this.render(this.renderTo);
15120         delete this.renderTo;
15121     }
15122 };
15123
15124 /** @private */
15125 Roo.Component.AUTO_ID = 1000;
15126
15127 Roo.extend(Roo.Component, Roo.util.Observable, {
15128     /**
15129      * @scope Roo.Component.prototype
15130      * @type {Boolean}
15131      * true if this component is hidden. Read-only.
15132      */
15133     hidden : false,
15134     /**
15135      * @type {Boolean}
15136      * true if this component is disabled. Read-only.
15137      */
15138     disabled : false,
15139     /**
15140      * @type {Boolean}
15141      * true if this component has been rendered. Read-only.
15142      */
15143     rendered : false,
15144     
15145     /** @cfg {String} disableClass
15146      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15147      */
15148     disabledClass : "x-item-disabled",
15149         /** @cfg {Boolean} allowDomMove
15150          * Whether the component can move the Dom node when rendering (defaults to true).
15151          */
15152     allowDomMove : true,
15153     /** @cfg {String} hideMode
15154      * How this component should hidden. Supported values are
15155      * "visibility" (css visibility), "offsets" (negative offset position) and
15156      * "display" (css display) - defaults to "display".
15157      */
15158     hideMode: 'display',
15159
15160     /** @private */
15161     ctype : "Roo.Component",
15162
15163     /**
15164      * @cfg {String} actionMode 
15165      * which property holds the element that used for  hide() / show() / disable() / enable()
15166      * default is 'el' 
15167      */
15168     actionMode : "el",
15169
15170     /** @private */
15171     getActionEl : function(){
15172         return this[this.actionMode];
15173     },
15174
15175     initComponent : Roo.emptyFn,
15176     /**
15177      * If this is a lazy rendering component, render it to its container element.
15178      * @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.
15179      */
15180     render : function(container, position){
15181         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15182             if(!container && this.el){
15183                 this.el = Roo.get(this.el);
15184                 container = this.el.dom.parentNode;
15185                 this.allowDomMove = false;
15186             }
15187             this.container = Roo.get(container);
15188             this.rendered = true;
15189             if(position !== undefined){
15190                 if(typeof position == 'number'){
15191                     position = this.container.dom.childNodes[position];
15192                 }else{
15193                     position = Roo.getDom(position);
15194                 }
15195             }
15196             this.onRender(this.container, position || null);
15197             if(this.cls){
15198                 this.el.addClass(this.cls);
15199                 delete this.cls;
15200             }
15201             if(this.style){
15202                 this.el.applyStyles(this.style);
15203                 delete this.style;
15204             }
15205             this.fireEvent("render", this);
15206             this.afterRender(this.container);
15207             if(this.hidden){
15208                 this.hide();
15209             }
15210             if(this.disabled){
15211                 this.disable();
15212             }
15213         }
15214         return this;
15215     },
15216
15217     /** @private */
15218     // default function is not really useful
15219     onRender : function(ct, position){
15220         if(this.el){
15221             this.el = Roo.get(this.el);
15222             if(this.allowDomMove !== false){
15223                 ct.dom.insertBefore(this.el.dom, position);
15224             }
15225         }
15226     },
15227
15228     /** @private */
15229     getAutoCreate : function(){
15230         var cfg = typeof this.autoCreate == "object" ?
15231                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15232         if(this.id && !cfg.id){
15233             cfg.id = this.id;
15234         }
15235         return cfg;
15236     },
15237
15238     /** @private */
15239     afterRender : Roo.emptyFn,
15240
15241     /**
15242      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15243      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15244      */
15245     destroy : function(){
15246         if(this.fireEvent("beforedestroy", this) !== false){
15247             this.purgeListeners();
15248             this.beforeDestroy();
15249             if(this.rendered){
15250                 this.el.removeAllListeners();
15251                 this.el.remove();
15252                 if(this.actionMode == "container"){
15253                     this.container.remove();
15254                 }
15255             }
15256             this.onDestroy();
15257             Roo.ComponentMgr.unregister(this);
15258             this.fireEvent("destroy", this);
15259         }
15260     },
15261
15262         /** @private */
15263     beforeDestroy : function(){
15264
15265     },
15266
15267         /** @private */
15268         onDestroy : function(){
15269
15270     },
15271
15272     /**
15273      * Returns the underlying {@link Roo.Element}.
15274      * @return {Roo.Element} The element
15275      */
15276     getEl : function(){
15277         return this.el;
15278     },
15279
15280     /**
15281      * Returns the id of this component.
15282      * @return {String}
15283      */
15284     getId : function(){
15285         return this.id;
15286     },
15287
15288     /**
15289      * Try to focus this component.
15290      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15291      * @return {Roo.Component} this
15292      */
15293     focus : function(selectText){
15294         if(this.rendered){
15295             this.el.focus();
15296             if(selectText === true){
15297                 this.el.dom.select();
15298             }
15299         }
15300         return this;
15301     },
15302
15303     /** @private */
15304     blur : function(){
15305         if(this.rendered){
15306             this.el.blur();
15307         }
15308         return this;
15309     },
15310
15311     /**
15312      * Disable this component.
15313      * @return {Roo.Component} this
15314      */
15315     disable : function(){
15316         if(this.rendered){
15317             this.onDisable();
15318         }
15319         this.disabled = true;
15320         this.fireEvent("disable", this);
15321         return this;
15322     },
15323
15324         // private
15325     onDisable : function(){
15326         this.getActionEl().addClass(this.disabledClass);
15327         this.el.dom.disabled = true;
15328     },
15329
15330     /**
15331      * Enable this component.
15332      * @return {Roo.Component} this
15333      */
15334     enable : function(){
15335         if(this.rendered){
15336             this.onEnable();
15337         }
15338         this.disabled = false;
15339         this.fireEvent("enable", this);
15340         return this;
15341     },
15342
15343         // private
15344     onEnable : function(){
15345         this.getActionEl().removeClass(this.disabledClass);
15346         this.el.dom.disabled = false;
15347     },
15348
15349     /**
15350      * Convenience function for setting disabled/enabled by boolean.
15351      * @param {Boolean} disabled
15352      */
15353     setDisabled : function(disabled){
15354         this[disabled ? "disable" : "enable"]();
15355     },
15356
15357     /**
15358      * Show this component.
15359      * @return {Roo.Component} this
15360      */
15361     show: function(){
15362         if(this.fireEvent("beforeshow", this) !== false){
15363             this.hidden = false;
15364             if(this.rendered){
15365                 this.onShow();
15366             }
15367             this.fireEvent("show", this);
15368         }
15369         return this;
15370     },
15371
15372     // private
15373     onShow : function(){
15374         var ae = this.getActionEl();
15375         if(this.hideMode == 'visibility'){
15376             ae.dom.style.visibility = "visible";
15377         }else if(this.hideMode == 'offsets'){
15378             ae.removeClass('x-hidden');
15379         }else{
15380             ae.dom.style.display = "";
15381         }
15382     },
15383
15384     /**
15385      * Hide this component.
15386      * @return {Roo.Component} this
15387      */
15388     hide: function(){
15389         if(this.fireEvent("beforehide", this) !== false){
15390             this.hidden = true;
15391             if(this.rendered){
15392                 this.onHide();
15393             }
15394             this.fireEvent("hide", this);
15395         }
15396         return this;
15397     },
15398
15399     // private
15400     onHide : function(){
15401         var ae = this.getActionEl();
15402         if(this.hideMode == 'visibility'){
15403             ae.dom.style.visibility = "hidden";
15404         }else if(this.hideMode == 'offsets'){
15405             ae.addClass('x-hidden');
15406         }else{
15407             ae.dom.style.display = "none";
15408         }
15409     },
15410
15411     /**
15412      * Convenience function to hide or show this component by boolean.
15413      * @param {Boolean} visible True to show, false to hide
15414      * @return {Roo.Component} this
15415      */
15416     setVisible: function(visible){
15417         if(visible) {
15418             this.show();
15419         }else{
15420             this.hide();
15421         }
15422         return this;
15423     },
15424
15425     /**
15426      * Returns true if this component is visible.
15427      */
15428     isVisible : function(){
15429         return this.getActionEl().isVisible();
15430     },
15431
15432     cloneConfig : function(overrides){
15433         overrides = overrides || {};
15434         var id = overrides.id || Roo.id();
15435         var cfg = Roo.applyIf(overrides, this.initialConfig);
15436         cfg.id = id; // prevent dup id
15437         return new this.constructor(cfg);
15438     }
15439 });/*
15440  * Based on:
15441  * Ext JS Library 1.1.1
15442  * Copyright(c) 2006-2007, Ext JS, LLC.
15443  *
15444  * Originally Released Under LGPL - original licence link has changed is not relivant.
15445  *
15446  * Fork - LGPL
15447  * <script type="text/javascript">
15448  */
15449
15450 /**
15451  * @class Roo.BoxComponent
15452  * @extends Roo.Component
15453  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15454  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15455  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15456  * layout containers.
15457  * @constructor
15458  * @param {Roo.Element/String/Object} config The configuration options.
15459  */
15460 Roo.BoxComponent = function(config){
15461     Roo.Component.call(this, config);
15462     this.addEvents({
15463         /**
15464          * @event resize
15465          * Fires after the component is resized.
15466              * @param {Roo.Component} this
15467              * @param {Number} adjWidth The box-adjusted width that was set
15468              * @param {Number} adjHeight The box-adjusted height that was set
15469              * @param {Number} rawWidth The width that was originally specified
15470              * @param {Number} rawHeight The height that was originally specified
15471              */
15472         resize : true,
15473         /**
15474          * @event move
15475          * Fires after the component is moved.
15476              * @param {Roo.Component} this
15477              * @param {Number} x The new x position
15478              * @param {Number} y The new y position
15479              */
15480         move : true
15481     });
15482 };
15483
15484 Roo.extend(Roo.BoxComponent, Roo.Component, {
15485     // private, set in afterRender to signify that the component has been rendered
15486     boxReady : false,
15487     // private, used to defer height settings to subclasses
15488     deferHeight: false,
15489     /** @cfg {Number} width
15490      * width (optional) size of component
15491      */
15492      /** @cfg {Number} height
15493      * height (optional) size of component
15494      */
15495      
15496     /**
15497      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15498      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15499      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15500      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15501      * @return {Roo.BoxComponent} this
15502      */
15503     setSize : function(w, h){
15504         // support for standard size objects
15505         if(typeof w == 'object'){
15506             h = w.height;
15507             w = w.width;
15508         }
15509         // not rendered
15510         if(!this.boxReady){
15511             this.width = w;
15512             this.height = h;
15513             return this;
15514         }
15515
15516         // prevent recalcs when not needed
15517         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15518             return this;
15519         }
15520         this.lastSize = {width: w, height: h};
15521
15522         var adj = this.adjustSize(w, h);
15523         var aw = adj.width, ah = adj.height;
15524         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15525             var rz = this.getResizeEl();
15526             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15527                 rz.setSize(aw, ah);
15528             }else if(!this.deferHeight && ah !== undefined){
15529                 rz.setHeight(ah);
15530             }else if(aw !== undefined){
15531                 rz.setWidth(aw);
15532             }
15533             this.onResize(aw, ah, w, h);
15534             this.fireEvent('resize', this, aw, ah, w, h);
15535         }
15536         return this;
15537     },
15538
15539     /**
15540      * Gets the current size of the component's underlying element.
15541      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15542      */
15543     getSize : function(){
15544         return this.el.getSize();
15545     },
15546
15547     /**
15548      * Gets the current XY position of the component's underlying element.
15549      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15550      * @return {Array} The XY position of the element (e.g., [100, 200])
15551      */
15552     getPosition : function(local){
15553         if(local === true){
15554             return [this.el.getLeft(true), this.el.getTop(true)];
15555         }
15556         return this.xy || this.el.getXY();
15557     },
15558
15559     /**
15560      * Gets the current box measurements of the component's underlying element.
15561      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15562      * @returns {Object} box An object in the format {x, y, width, height}
15563      */
15564     getBox : function(local){
15565         var s = this.el.getSize();
15566         if(local){
15567             s.x = this.el.getLeft(true);
15568             s.y = this.el.getTop(true);
15569         }else{
15570             var xy = this.xy || this.el.getXY();
15571             s.x = xy[0];
15572             s.y = xy[1];
15573         }
15574         return s;
15575     },
15576
15577     /**
15578      * Sets the current box measurements of the component's underlying element.
15579      * @param {Object} box An object in the format {x, y, width, height}
15580      * @returns {Roo.BoxComponent} this
15581      */
15582     updateBox : function(box){
15583         this.setSize(box.width, box.height);
15584         this.setPagePosition(box.x, box.y);
15585         return this;
15586     },
15587
15588     // protected
15589     getResizeEl : function(){
15590         return this.resizeEl || this.el;
15591     },
15592
15593     // protected
15594     getPositionEl : function(){
15595         return this.positionEl || this.el;
15596     },
15597
15598     /**
15599      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15600      * This method fires the move event.
15601      * @param {Number} left The new left
15602      * @param {Number} top The new top
15603      * @returns {Roo.BoxComponent} this
15604      */
15605     setPosition : function(x, y){
15606         this.x = x;
15607         this.y = y;
15608         if(!this.boxReady){
15609             return this;
15610         }
15611         var adj = this.adjustPosition(x, y);
15612         var ax = adj.x, ay = adj.y;
15613
15614         var el = this.getPositionEl();
15615         if(ax !== undefined || ay !== undefined){
15616             if(ax !== undefined && ay !== undefined){
15617                 el.setLeftTop(ax, ay);
15618             }else if(ax !== undefined){
15619                 el.setLeft(ax);
15620             }else if(ay !== undefined){
15621                 el.setTop(ay);
15622             }
15623             this.onPosition(ax, ay);
15624             this.fireEvent('move', this, ax, ay);
15625         }
15626         return this;
15627     },
15628
15629     /**
15630      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15631      * This method fires the move event.
15632      * @param {Number} x The new x position
15633      * @param {Number} y The new y position
15634      * @returns {Roo.BoxComponent} this
15635      */
15636     setPagePosition : function(x, y){
15637         this.pageX = x;
15638         this.pageY = y;
15639         if(!this.boxReady){
15640             return;
15641         }
15642         if(x === undefined || y === undefined){ // cannot translate undefined points
15643             return;
15644         }
15645         var p = this.el.translatePoints(x, y);
15646         this.setPosition(p.left, p.top);
15647         return this;
15648     },
15649
15650     // private
15651     onRender : function(ct, position){
15652         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15653         if(this.resizeEl){
15654             this.resizeEl = Roo.get(this.resizeEl);
15655         }
15656         if(this.positionEl){
15657             this.positionEl = Roo.get(this.positionEl);
15658         }
15659     },
15660
15661     // private
15662     afterRender : function(){
15663         Roo.BoxComponent.superclass.afterRender.call(this);
15664         this.boxReady = true;
15665         this.setSize(this.width, this.height);
15666         if(this.x || this.y){
15667             this.setPosition(this.x, this.y);
15668         }
15669         if(this.pageX || this.pageY){
15670             this.setPagePosition(this.pageX, this.pageY);
15671         }
15672     },
15673
15674     /**
15675      * Force the component's size to recalculate based on the underlying element's current height and width.
15676      * @returns {Roo.BoxComponent} this
15677      */
15678     syncSize : function(){
15679         delete this.lastSize;
15680         this.setSize(this.el.getWidth(), this.el.getHeight());
15681         return this;
15682     },
15683
15684     /**
15685      * Called after the component is resized, this method is empty by default but can be implemented by any
15686      * subclass that needs to perform custom logic after a resize occurs.
15687      * @param {Number} adjWidth The box-adjusted width that was set
15688      * @param {Number} adjHeight The box-adjusted height that was set
15689      * @param {Number} rawWidth The width that was originally specified
15690      * @param {Number} rawHeight The height that was originally specified
15691      */
15692     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15693
15694     },
15695
15696     /**
15697      * Called after the component is moved, this method is empty by default but can be implemented by any
15698      * subclass that needs to perform custom logic after a move occurs.
15699      * @param {Number} x The new x position
15700      * @param {Number} y The new y position
15701      */
15702     onPosition : function(x, y){
15703
15704     },
15705
15706     // private
15707     adjustSize : function(w, h){
15708         if(this.autoWidth){
15709             w = 'auto';
15710         }
15711         if(this.autoHeight){
15712             h = 'auto';
15713         }
15714         return {width : w, height: h};
15715     },
15716
15717     // private
15718     adjustPosition : function(x, y){
15719         return {x : x, y: y};
15720     }
15721 });/*
15722  * Original code for Roojs - LGPL
15723  * <script type="text/javascript">
15724  */
15725  
15726 /**
15727  * @class Roo.XComponent
15728  * A delayed Element creator...
15729  * Or a way to group chunks of interface together.
15730  * 
15731  * Mypart.xyx = new Roo.XComponent({
15732
15733     parent : 'Mypart.xyz', // empty == document.element.!!
15734     order : '001',
15735     name : 'xxxx'
15736     region : 'xxxx'
15737     disabled : function() {} 
15738      
15739     tree : function() { // return an tree of xtype declared components
15740         var MODULE = this;
15741         return 
15742         {
15743             xtype : 'NestedLayoutPanel',
15744             // technicall
15745         }
15746      ]
15747  *})
15748  *
15749  *
15750  * It can be used to build a big heiracy, with parent etc.
15751  * or you can just use this to render a single compoent to a dom element
15752  * MYPART.render(Roo.Element | String(id) | dom_element )
15753  * 
15754  * @extends Roo.util.Observable
15755  * @constructor
15756  * @param cfg {Object} configuration of component
15757  * 
15758  */
15759 Roo.XComponent = function(cfg) {
15760     Roo.apply(this, cfg);
15761     this.addEvents({ 
15762         /**
15763              * @event built
15764              * Fires when this the componnt is built
15765              * @param {Roo.XComponent} c the component
15766              */
15767         'built' : true
15768         
15769     });
15770     this.region = this.region || 'center'; // default..
15771     Roo.XComponent.register(this);
15772     this.modules = false;
15773     this.el = false; // where the layout goes..
15774     
15775     
15776 }
15777 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15778     /**
15779      * @property el
15780      * The created element (with Roo.factory())
15781      * @type {Roo.Layout}
15782      */
15783     el  : false,
15784     
15785     /**
15786      * @property el
15787      * for BC  - use el in new code
15788      * @type {Roo.Layout}
15789      */
15790     panel : false,
15791     
15792     /**
15793      * @property layout
15794      * for BC  - use el in new code
15795      * @type {Roo.Layout}
15796      */
15797     layout : false,
15798     
15799      /**
15800      * @cfg {Function|boolean} disabled
15801      * If this module is disabled by some rule, return true from the funtion
15802      */
15803     disabled : false,
15804     
15805     /**
15806      * @cfg {String} parent 
15807      * Name of parent element which it get xtype added to..
15808      */
15809     parent: false,
15810     
15811     /**
15812      * @cfg {String} order
15813      * Used to set the order in which elements are created (usefull for multiple tabs)
15814      */
15815     
15816     order : false,
15817     /**
15818      * @cfg {String} name
15819      * String to display while loading.
15820      */
15821     name : false,
15822     /**
15823      * @cfg {String} region
15824      * Region to render component to (defaults to center)
15825      */
15826     region : 'center',
15827     
15828     /**
15829      * @cfg {Array} items
15830      * A single item array - the first element is the root of the tree..
15831      * It's done this way to stay compatible with the Xtype system...
15832      */
15833     items : false,
15834     
15835     /**
15836      * @property _tree
15837      * The method that retuns the tree of parts that make up this compoennt 
15838      * @type {function}
15839      */
15840     _tree  : false,
15841     
15842      /**
15843      * render
15844      * render element to dom or tree
15845      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15846      */
15847     
15848     render : function(el)
15849     {
15850         
15851         el = el || false;
15852         var hp = this.parent ? 1 : 0;
15853         
15854         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15855             // if parent is a '#.....' string, then let's use that..
15856             var ename = this.parent.substr(1)
15857             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15858             el = Roo.get(ename);
15859             if (!el && !this.parent) {
15860                 Roo.log("Warning - element can not be found :#" + ename );
15861                 return;
15862             }
15863         }
15864         
15865         
15866         if (!this.parent) {
15867             
15868             el = el ? Roo.get(el) : false;      
15869             
15870             // it's a top level one..
15871             this.parent =  {
15872                 el : new Roo.BorderLayout(el || document.body, {
15873                 
15874                      center: {
15875                          titlebar: false,
15876                          autoScroll:false,
15877                          closeOnTab: true,
15878                          tabPosition: 'top',
15879                           //resizeTabs: true,
15880                          alwaysShowTabs: el && hp? false :  true,
15881                          hideTabs: el || !hp ? true :  false,
15882                          minTabWidth: 140
15883                      }
15884                  })
15885             }
15886         }
15887         
15888                 if (!this.parent.el) {
15889                         // probably an old style ctor, which has been disabled.
15890                         return;
15891                         
15892                 }
15893                 // The 'tree' method is  '_tree now' 
15894             
15895         var tree = this._tree ? this._tree() : this.tree();
15896         tree.region = tree.region || this.region;
15897         if (this.parent.el === true) {
15898             // bootstrap... - body..
15899             this.parent.el = Roo.factory(tree);
15900         }
15901         this.el = this.parent.el.addxtype(tree);
15902         this.fireEvent('built', this);
15903         
15904         this.panel = this.el;
15905         this.layout = this.panel.layout;
15906                 this.parentLayout = this.parent.layout  || false;  
15907          
15908     }
15909     
15910 });
15911
15912 Roo.apply(Roo.XComponent, {
15913     /**
15914      * @property  hideProgress
15915      * true to disable the building progress bar.. usefull on single page renders.
15916      * @type Boolean
15917      */
15918     hideProgress : false,
15919     /**
15920      * @property  buildCompleted
15921      * True when the builder has completed building the interface.
15922      * @type Boolean
15923      */
15924     buildCompleted : false,
15925      
15926     /**
15927      * @property  topModule
15928      * the upper most module - uses document.element as it's constructor.
15929      * @type Object
15930      */
15931      
15932     topModule  : false,
15933       
15934     /**
15935      * @property  modules
15936      * array of modules to be created by registration system.
15937      * @type {Array} of Roo.XComponent
15938      */
15939     
15940     modules : [],
15941     /**
15942      * @property  elmodules
15943      * array of modules to be created by which use #ID 
15944      * @type {Array} of Roo.XComponent
15945      */
15946      
15947     elmodules : [],
15948
15949     
15950     /**
15951      * Register components to be built later.
15952      *
15953      * This solves the following issues
15954      * - Building is not done on page load, but after an authentication process has occured.
15955      * - Interface elements are registered on page load
15956      * - Parent Interface elements may not be loaded before child, so this handles that..
15957      * 
15958      *
15959      * example:
15960      * 
15961      * MyApp.register({
15962           order : '000001',
15963           module : 'Pman.Tab.projectMgr',
15964           region : 'center',
15965           parent : 'Pman.layout',
15966           disabled : false,  // or use a function..
15967         })
15968      
15969      * * @param {Object} details about module
15970      */
15971     register : function(obj) {
15972                 
15973         Roo.XComponent.event.fireEvent('register', obj);
15974         switch(typeof(obj.disabled) ) {
15975                 
15976             case 'undefined':
15977                 break;
15978             
15979             case 'function':
15980                 if ( obj.disabled() ) {
15981                         return;
15982                 }
15983                 break;
15984             
15985             default:
15986                 if (obj.disabled) {
15987                         return;
15988                 }
15989                 break;
15990         }
15991                 
15992         this.modules.push(obj);
15993          
15994     },
15995     /**
15996      * convert a string to an object..
15997      * eg. 'AAA.BBB' -> finds AAA.BBB
15998
15999      */
16000     
16001     toObject : function(str)
16002     {
16003         if (!str || typeof(str) == 'object') {
16004             return str;
16005         }
16006         if (str.substring(0,1) == '#') {
16007             return str;
16008         }
16009
16010         var ar = str.split('.');
16011         var rt, o;
16012         rt = ar.shift();
16013             /** eval:var:o */
16014         try {
16015             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16016         } catch (e) {
16017             throw "Module not found : " + str;
16018         }
16019         
16020         if (o === false) {
16021             throw "Module not found : " + str;
16022         }
16023         Roo.each(ar, function(e) {
16024             if (typeof(o[e]) == 'undefined') {
16025                 throw "Module not found : " + str;
16026             }
16027             o = o[e];
16028         });
16029         
16030         return o;
16031         
16032     },
16033     
16034     
16035     /**
16036      * move modules into their correct place in the tree..
16037      * 
16038      */
16039     preBuild : function ()
16040     {
16041         var _t = this;
16042         Roo.each(this.modules , function (obj)
16043         {
16044             Roo.XComponent.event.fireEvent('beforebuild', obj);
16045             
16046             var opar = obj.parent;
16047             try { 
16048                 obj.parent = this.toObject(opar);
16049             } catch(e) {
16050                 Roo.log("parent:toObject failed: " + e.toString());
16051                 return;
16052             }
16053             
16054             if (!obj.parent) {
16055                 Roo.debug && Roo.log("GOT top level module");
16056                 Roo.debug && Roo.log(obj);
16057                 obj.modules = new Roo.util.MixedCollection(false, 
16058                     function(o) { return o.order + '' }
16059                 );
16060                 this.topModule = obj;
16061                 return;
16062             }
16063                         // parent is a string (usually a dom element name..)
16064             if (typeof(obj.parent) == 'string') {
16065                 this.elmodules.push(obj);
16066                 return;
16067             }
16068             if (obj.parent.constructor != Roo.XComponent) {
16069                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16070             }
16071             if (!obj.parent.modules) {
16072                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16073                     function(o) { return o.order + '' }
16074                 );
16075             }
16076             if (obj.parent.disabled) {
16077                 obj.disabled = true;
16078             }
16079             obj.parent.modules.add(obj);
16080         }, this);
16081     },
16082     
16083      /**
16084      * make a list of modules to build.
16085      * @return {Array} list of modules. 
16086      */ 
16087     
16088     buildOrder : function()
16089     {
16090         var _this = this;
16091         var cmp = function(a,b) {   
16092             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16093         };
16094         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16095             throw "No top level modules to build";
16096         }
16097         
16098         // make a flat list in order of modules to build.
16099         var mods = this.topModule ? [ this.topModule ] : [];
16100                 
16101         
16102         // elmodules (is a list of DOM based modules )
16103         Roo.each(this.elmodules, function(e) {
16104             mods.push(e);
16105             if (!this.topModule &&
16106                 typeof(e.parent) == 'string' &&
16107                 e.parent.substring(0,1) == '#' &&
16108                 Roo.get(e.parent.substr(1))
16109                ) {
16110                 
16111                 _this.topModule = e;
16112             }
16113             
16114         });
16115
16116         
16117         // add modules to their parents..
16118         var addMod = function(m) {
16119             Roo.debug && Roo.log("build Order: add: " + m.name);
16120                 
16121             mods.push(m);
16122             if (m.modules && !m.disabled) {
16123                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16124                 m.modules.keySort('ASC',  cmp );
16125                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16126     
16127                 m.modules.each(addMod);
16128             } else {
16129                 Roo.debug && Roo.log("build Order: no child modules");
16130             }
16131             // not sure if this is used any more..
16132             if (m.finalize) {
16133                 m.finalize.name = m.name + " (clean up) ";
16134                 mods.push(m.finalize);
16135             }
16136             
16137         }
16138         if (this.topModule && this.topModule.modules) { 
16139             this.topModule.modules.keySort('ASC',  cmp );
16140             this.topModule.modules.each(addMod);
16141         } 
16142         return mods;
16143     },
16144     
16145      /**
16146      * Build the registered modules.
16147      * @param {Object} parent element.
16148      * @param {Function} optional method to call after module has been added.
16149      * 
16150      */ 
16151    
16152     build : function() 
16153     {
16154         
16155         this.preBuild();
16156         var mods = this.buildOrder();
16157       
16158         //this.allmods = mods;
16159         //Roo.debug && Roo.log(mods);
16160         //return;
16161         if (!mods.length) { // should not happen
16162             throw "NO modules!!!";
16163         }
16164         
16165         
16166         var msg = "Building Interface...";
16167         // flash it up as modal - so we store the mask!?
16168         if (!this.hideProgress && Roo.MessageBox) {
16169             Roo.MessageBox.show({ title: 'loading' });
16170             Roo.MessageBox.show({
16171                title: "Please wait...",
16172                msg: msg,
16173                width:450,
16174                progress:true,
16175                closable:false,
16176                modal: false
16177               
16178             });
16179         }
16180         var total = mods.length;
16181         
16182         var _this = this;
16183         var progressRun = function() {
16184             if (!mods.length) {
16185                 Roo.debug && Roo.log('hide?');
16186                 if (!this.hideProgress && Roo.MessageBox) {
16187                     Roo.MessageBox.hide();
16188                 }
16189                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16190                 
16191                 // THE END...
16192                 return false;   
16193             }
16194             
16195             var m = mods.shift();
16196             
16197             
16198             Roo.debug && Roo.log(m);
16199             // not sure if this is supported any more.. - modules that are are just function
16200             if (typeof(m) == 'function') { 
16201                 m.call(this);
16202                 return progressRun.defer(10, _this);
16203             } 
16204             
16205             
16206             msg = "Building Interface " + (total  - mods.length) + 
16207                     " of " + total + 
16208                     (m.name ? (' - ' + m.name) : '');
16209                         Roo.debug && Roo.log(msg);
16210             if (!this.hideProgress &&  Roo.MessageBox) { 
16211                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16212             }
16213             
16214          
16215             // is the module disabled?
16216             var disabled = (typeof(m.disabled) == 'function') ?
16217                 m.disabled.call(m.module.disabled) : m.disabled;    
16218             
16219             
16220             if (disabled) {
16221                 return progressRun(); // we do not update the display!
16222             }
16223             
16224             // now build 
16225             
16226                         
16227                         
16228             m.render();
16229             // it's 10 on top level, and 1 on others??? why...
16230             return progressRun.defer(10, _this);
16231              
16232         }
16233         progressRun.defer(1, _this);
16234      
16235         
16236         
16237     },
16238         
16239         
16240         /**
16241          * Event Object.
16242          *
16243          *
16244          */
16245         event: false, 
16246     /**
16247          * wrapper for event.on - aliased later..  
16248          * Typically use to register a event handler for register:
16249          *
16250          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16251          *
16252          */
16253     on : false
16254    
16255     
16256     
16257 });
16258
16259 Roo.XComponent.event = new Roo.util.Observable({
16260                 events : { 
16261                         /**
16262                          * @event register
16263                          * Fires when an Component is registered,
16264                          * set the disable property on the Component to stop registration.
16265                          * @param {Roo.XComponent} c the component being registerd.
16266                          * 
16267                          */
16268                         'register' : true,
16269             /**
16270                          * @event beforebuild
16271                          * Fires before each Component is built
16272                          * can be used to apply permissions.
16273                          * @param {Roo.XComponent} c the component being registerd.
16274                          * 
16275                          */
16276                         'beforebuild' : true,
16277                         /**
16278                          * @event buildcomplete
16279                          * Fires on the top level element when all elements have been built
16280                          * @param {Roo.XComponent} the top level component.
16281                          */
16282                         'buildcomplete' : true
16283                         
16284                 }
16285 });
16286
16287 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16288  /*
16289  * Based on:
16290  * Ext JS Library 1.1.1
16291  * Copyright(c) 2006-2007, Ext JS, LLC.
16292  *
16293  * Originally Released Under LGPL - original licence link has changed is not relivant.
16294  *
16295  * Fork - LGPL
16296  * <script type="text/javascript">
16297  */
16298
16299
16300
16301 /*
16302  * These classes are derivatives of the similarly named classes in the YUI Library.
16303  * The original license:
16304  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16305  * Code licensed under the BSD License:
16306  * http://developer.yahoo.net/yui/license.txt
16307  */
16308
16309 (function() {
16310
16311 var Event=Roo.EventManager;
16312 var Dom=Roo.lib.Dom;
16313
16314 /**
16315  * @class Roo.dd.DragDrop
16316  * @extends Roo.util.Observable
16317  * Defines the interface and base operation of items that that can be
16318  * dragged or can be drop targets.  It was designed to be extended, overriding
16319  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16320  * Up to three html elements can be associated with a DragDrop instance:
16321  * <ul>
16322  * <li>linked element: the element that is passed into the constructor.
16323  * This is the element which defines the boundaries for interaction with
16324  * other DragDrop objects.</li>
16325  * <li>handle element(s): The drag operation only occurs if the element that
16326  * was clicked matches a handle element.  By default this is the linked
16327  * element, but there are times that you will want only a portion of the
16328  * linked element to initiate the drag operation, and the setHandleElId()
16329  * method provides a way to define this.</li>
16330  * <li>drag element: this represents the element that would be moved along
16331  * with the cursor during a drag operation.  By default, this is the linked
16332  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16333  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16334  * </li>
16335  * </ul>
16336  * This class should not be instantiated until the onload event to ensure that
16337  * the associated elements are available.
16338  * The following would define a DragDrop obj that would interact with any
16339  * other DragDrop obj in the "group1" group:
16340  * <pre>
16341  *  dd = new Roo.dd.DragDrop("div1", "group1");
16342  * </pre>
16343  * Since none of the event handlers have been implemented, nothing would
16344  * actually happen if you were to run the code above.  Normally you would
16345  * override this class or one of the default implementations, but you can
16346  * also override the methods you want on an instance of the class...
16347  * <pre>
16348  *  dd.onDragDrop = function(e, id) {
16349  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16350  *  }
16351  * </pre>
16352  * @constructor
16353  * @param {String} id of the element that is linked to this instance
16354  * @param {String} sGroup the group of related DragDrop objects
16355  * @param {object} config an object containing configurable attributes
16356  *                Valid properties for DragDrop:
16357  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16358  */
16359 Roo.dd.DragDrop = function(id, sGroup, config) {
16360     if (id) {
16361         this.init(id, sGroup, config);
16362     }
16363     
16364 };
16365
16366 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16367
16368     /**
16369      * The id of the element associated with this object.  This is what we
16370      * refer to as the "linked element" because the size and position of
16371      * this element is used to determine when the drag and drop objects have
16372      * interacted.
16373      * @property id
16374      * @type String
16375      */
16376     id: null,
16377
16378     /**
16379      * Configuration attributes passed into the constructor
16380      * @property config
16381      * @type object
16382      */
16383     config: null,
16384
16385     /**
16386      * The id of the element that will be dragged.  By default this is same
16387      * as the linked element , but could be changed to another element. Ex:
16388      * Roo.dd.DDProxy
16389      * @property dragElId
16390      * @type String
16391      * @private
16392      */
16393     dragElId: null,
16394
16395     /**
16396      * the id of the element that initiates the drag operation.  By default
16397      * this is the linked element, but could be changed to be a child of this
16398      * element.  This lets us do things like only starting the drag when the
16399      * header element within the linked html element is clicked.
16400      * @property handleElId
16401      * @type String
16402      * @private
16403      */
16404     handleElId: null,
16405
16406     /**
16407      * An associative array of HTML tags that will be ignored if clicked.
16408      * @property invalidHandleTypes
16409      * @type {string: string}
16410      */
16411     invalidHandleTypes: null,
16412
16413     /**
16414      * An associative array of ids for elements that will be ignored if clicked
16415      * @property invalidHandleIds
16416      * @type {string: string}
16417      */
16418     invalidHandleIds: null,
16419
16420     /**
16421      * An indexted array of css class names for elements that will be ignored
16422      * if clicked.
16423      * @property invalidHandleClasses
16424      * @type string[]
16425      */
16426     invalidHandleClasses: null,
16427
16428     /**
16429      * The linked element's absolute X position at the time the drag was
16430      * started
16431      * @property startPageX
16432      * @type int
16433      * @private
16434      */
16435     startPageX: 0,
16436
16437     /**
16438      * The linked element's absolute X position at the time the drag was
16439      * started
16440      * @property startPageY
16441      * @type int
16442      * @private
16443      */
16444     startPageY: 0,
16445
16446     /**
16447      * The group defines a logical collection of DragDrop objects that are
16448      * related.  Instances only get events when interacting with other
16449      * DragDrop object in the same group.  This lets us define multiple
16450      * groups using a single DragDrop subclass if we want.
16451      * @property groups
16452      * @type {string: string}
16453      */
16454     groups: null,
16455
16456     /**
16457      * Individual drag/drop instances can be locked.  This will prevent
16458      * onmousedown start drag.
16459      * @property locked
16460      * @type boolean
16461      * @private
16462      */
16463     locked: false,
16464
16465     /**
16466      * Lock this instance
16467      * @method lock
16468      */
16469     lock: function() { this.locked = true; },
16470
16471     /**
16472      * Unlock this instace
16473      * @method unlock
16474      */
16475     unlock: function() { this.locked = false; },
16476
16477     /**
16478      * By default, all insances can be a drop target.  This can be disabled by
16479      * setting isTarget to false.
16480      * @method isTarget
16481      * @type boolean
16482      */
16483     isTarget: true,
16484
16485     /**
16486      * The padding configured for this drag and drop object for calculating
16487      * the drop zone intersection with this object.
16488      * @method padding
16489      * @type int[]
16490      */
16491     padding: null,
16492
16493     /**
16494      * Cached reference to the linked element
16495      * @property _domRef
16496      * @private
16497      */
16498     _domRef: null,
16499
16500     /**
16501      * Internal typeof flag
16502      * @property __ygDragDrop
16503      * @private
16504      */
16505     __ygDragDrop: true,
16506
16507     /**
16508      * Set to true when horizontal contraints are applied
16509      * @property constrainX
16510      * @type boolean
16511      * @private
16512      */
16513     constrainX: false,
16514
16515     /**
16516      * Set to true when vertical contraints are applied
16517      * @property constrainY
16518      * @type boolean
16519      * @private
16520      */
16521     constrainY: false,
16522
16523     /**
16524      * The left constraint
16525      * @property minX
16526      * @type int
16527      * @private
16528      */
16529     minX: 0,
16530
16531     /**
16532      * The right constraint
16533      * @property maxX
16534      * @type int
16535      * @private
16536      */
16537     maxX: 0,
16538
16539     /**
16540      * The up constraint
16541      * @property minY
16542      * @type int
16543      * @type int
16544      * @private
16545      */
16546     minY: 0,
16547
16548     /**
16549      * The down constraint
16550      * @property maxY
16551      * @type int
16552      * @private
16553      */
16554     maxY: 0,
16555
16556     /**
16557      * Maintain offsets when we resetconstraints.  Set to true when you want
16558      * the position of the element relative to its parent to stay the same
16559      * when the page changes
16560      *
16561      * @property maintainOffset
16562      * @type boolean
16563      */
16564     maintainOffset: false,
16565
16566     /**
16567      * Array of pixel locations the element will snap to if we specified a
16568      * horizontal graduation/interval.  This array is generated automatically
16569      * when you define a tick interval.
16570      * @property xTicks
16571      * @type int[]
16572      */
16573     xTicks: null,
16574
16575     /**
16576      * Array of pixel locations the element will snap to if we specified a
16577      * vertical graduation/interval.  This array is generated automatically
16578      * when you define a tick interval.
16579      * @property yTicks
16580      * @type int[]
16581      */
16582     yTicks: null,
16583
16584     /**
16585      * By default the drag and drop instance will only respond to the primary
16586      * button click (left button for a right-handed mouse).  Set to true to
16587      * allow drag and drop to start with any mouse click that is propogated
16588      * by the browser
16589      * @property primaryButtonOnly
16590      * @type boolean
16591      */
16592     primaryButtonOnly: true,
16593
16594     /**
16595      * The availabe property is false until the linked dom element is accessible.
16596      * @property available
16597      * @type boolean
16598      */
16599     available: false,
16600
16601     /**
16602      * By default, drags can only be initiated if the mousedown occurs in the
16603      * region the linked element is.  This is done in part to work around a
16604      * bug in some browsers that mis-report the mousedown if the previous
16605      * mouseup happened outside of the window.  This property is set to true
16606      * if outer handles are defined.
16607      *
16608      * @property hasOuterHandles
16609      * @type boolean
16610      * @default false
16611      */
16612     hasOuterHandles: false,
16613
16614     /**
16615      * Code that executes immediately before the startDrag event
16616      * @method b4StartDrag
16617      * @private
16618      */
16619     b4StartDrag: function(x, y) { },
16620
16621     /**
16622      * Abstract method called after a drag/drop object is clicked
16623      * and the drag or mousedown time thresholds have beeen met.
16624      * @method startDrag
16625      * @param {int} X click location
16626      * @param {int} Y click location
16627      */
16628     startDrag: function(x, y) { /* override this */ },
16629
16630     /**
16631      * Code that executes immediately before the onDrag event
16632      * @method b4Drag
16633      * @private
16634      */
16635     b4Drag: function(e) { },
16636
16637     /**
16638      * Abstract method called during the onMouseMove event while dragging an
16639      * object.
16640      * @method onDrag
16641      * @param {Event} e the mousemove event
16642      */
16643     onDrag: function(e) { /* override this */ },
16644
16645     /**
16646      * Abstract method called when this element fist begins hovering over
16647      * another DragDrop obj
16648      * @method onDragEnter
16649      * @param {Event} e the mousemove event
16650      * @param {String|DragDrop[]} id In POINT mode, the element
16651      * id this is hovering over.  In INTERSECT mode, an array of one or more
16652      * dragdrop items being hovered over.
16653      */
16654     onDragEnter: function(e, id) { /* override this */ },
16655
16656     /**
16657      * Code that executes immediately before the onDragOver event
16658      * @method b4DragOver
16659      * @private
16660      */
16661     b4DragOver: function(e) { },
16662
16663     /**
16664      * Abstract method called when this element is hovering over another
16665      * DragDrop obj
16666      * @method onDragOver
16667      * @param {Event} e the mousemove event
16668      * @param {String|DragDrop[]} id In POINT mode, the element
16669      * id this is hovering over.  In INTERSECT mode, an array of dd items
16670      * being hovered over.
16671      */
16672     onDragOver: function(e, id) { /* override this */ },
16673
16674     /**
16675      * Code that executes immediately before the onDragOut event
16676      * @method b4DragOut
16677      * @private
16678      */
16679     b4DragOut: function(e) { },
16680
16681     /**
16682      * Abstract method called when we are no longer hovering over an element
16683      * @method onDragOut
16684      * @param {Event} e the mousemove event
16685      * @param {String|DragDrop[]} id In POINT mode, the element
16686      * id this was hovering over.  In INTERSECT mode, an array of dd items
16687      * that the mouse is no longer over.
16688      */
16689     onDragOut: function(e, id) { /* override this */ },
16690
16691     /**
16692      * Code that executes immediately before the onDragDrop event
16693      * @method b4DragDrop
16694      * @private
16695      */
16696     b4DragDrop: function(e) { },
16697
16698     /**
16699      * Abstract method called when this item is dropped on another DragDrop
16700      * obj
16701      * @method onDragDrop
16702      * @param {Event} e the mouseup event
16703      * @param {String|DragDrop[]} id In POINT mode, the element
16704      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16705      * was dropped on.
16706      */
16707     onDragDrop: function(e, id) { /* override this */ },
16708
16709     /**
16710      * Abstract method called when this item is dropped on an area with no
16711      * drop target
16712      * @method onInvalidDrop
16713      * @param {Event} e the mouseup event
16714      */
16715     onInvalidDrop: function(e) { /* override this */ },
16716
16717     /**
16718      * Code that executes immediately before the endDrag event
16719      * @method b4EndDrag
16720      * @private
16721      */
16722     b4EndDrag: function(e) { },
16723
16724     /**
16725      * Fired when we are done dragging the object
16726      * @method endDrag
16727      * @param {Event} e the mouseup event
16728      */
16729     endDrag: function(e) { /* override this */ },
16730
16731     /**
16732      * Code executed immediately before the onMouseDown event
16733      * @method b4MouseDown
16734      * @param {Event} e the mousedown event
16735      * @private
16736      */
16737     b4MouseDown: function(e) {  },
16738
16739     /**
16740      * Event handler that fires when a drag/drop obj gets a mousedown
16741      * @method onMouseDown
16742      * @param {Event} e the mousedown event
16743      */
16744     onMouseDown: function(e) { /* override this */ },
16745
16746     /**
16747      * Event handler that fires when a drag/drop obj gets a mouseup
16748      * @method onMouseUp
16749      * @param {Event} e the mouseup event
16750      */
16751     onMouseUp: function(e) { /* override this */ },
16752
16753     /**
16754      * Override the onAvailable method to do what is needed after the initial
16755      * position was determined.
16756      * @method onAvailable
16757      */
16758     onAvailable: function () {
16759     },
16760
16761     /*
16762      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16763      * @type Object
16764      */
16765     defaultPadding : {left:0, right:0, top:0, bottom:0},
16766
16767     /*
16768      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16769  *
16770  * Usage:
16771  <pre><code>
16772  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16773                 { dragElId: "existingProxyDiv" });
16774  dd.startDrag = function(){
16775      this.constrainTo("parent-id");
16776  };
16777  </code></pre>
16778  * Or you can initalize it using the {@link Roo.Element} object:
16779  <pre><code>
16780  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16781      startDrag : function(){
16782          this.constrainTo("parent-id");
16783      }
16784  });
16785  </code></pre>
16786      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16787      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16788      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16789      * an object containing the sides to pad. For example: {right:10, bottom:10}
16790      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16791      */
16792     constrainTo : function(constrainTo, pad, inContent){
16793         if(typeof pad == "number"){
16794             pad = {left: pad, right:pad, top:pad, bottom:pad};
16795         }
16796         pad = pad || this.defaultPadding;
16797         var b = Roo.get(this.getEl()).getBox();
16798         var ce = Roo.get(constrainTo);
16799         var s = ce.getScroll();
16800         var c, cd = ce.dom;
16801         if(cd == document.body){
16802             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16803         }else{
16804             xy = ce.getXY();
16805             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16806         }
16807
16808
16809         var topSpace = b.y - c.y;
16810         var leftSpace = b.x - c.x;
16811
16812         this.resetConstraints();
16813         this.setXConstraint(leftSpace - (pad.left||0), // left
16814                 c.width - leftSpace - b.width - (pad.right||0) //right
16815         );
16816         this.setYConstraint(topSpace - (pad.top||0), //top
16817                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16818         );
16819     },
16820
16821     /**
16822      * Returns a reference to the linked element
16823      * @method getEl
16824      * @return {HTMLElement} the html element
16825      */
16826     getEl: function() {
16827         if (!this._domRef) {
16828             this._domRef = Roo.getDom(this.id);
16829         }
16830
16831         return this._domRef;
16832     },
16833
16834     /**
16835      * Returns a reference to the actual element to drag.  By default this is
16836      * the same as the html element, but it can be assigned to another
16837      * element. An example of this can be found in Roo.dd.DDProxy
16838      * @method getDragEl
16839      * @return {HTMLElement} the html element
16840      */
16841     getDragEl: function() {
16842         return Roo.getDom(this.dragElId);
16843     },
16844
16845     /**
16846      * Sets up the DragDrop object.  Must be called in the constructor of any
16847      * Roo.dd.DragDrop subclass
16848      * @method init
16849      * @param id the id of the linked element
16850      * @param {String} sGroup the group of related items
16851      * @param {object} config configuration attributes
16852      */
16853     init: function(id, sGroup, config) {
16854         this.initTarget(id, sGroup, config);
16855         if (!Roo.isTouch) {
16856             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16857         }
16858         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16859         // Event.on(this.id, "selectstart", Event.preventDefault);
16860     },
16861
16862     /**
16863      * Initializes Targeting functionality only... the object does not
16864      * get a mousedown handler.
16865      * @method initTarget
16866      * @param id the id of the linked element
16867      * @param {String} sGroup the group of related items
16868      * @param {object} config configuration attributes
16869      */
16870     initTarget: function(id, sGroup, config) {
16871
16872         // configuration attributes
16873         this.config = config || {};
16874
16875         // create a local reference to the drag and drop manager
16876         this.DDM = Roo.dd.DDM;
16877         // initialize the groups array
16878         this.groups = {};
16879
16880         // assume that we have an element reference instead of an id if the
16881         // parameter is not a string
16882         if (typeof id !== "string") {
16883             id = Roo.id(id);
16884         }
16885
16886         // set the id
16887         this.id = id;
16888
16889         // add to an interaction group
16890         this.addToGroup((sGroup) ? sGroup : "default");
16891
16892         // We don't want to register this as the handle with the manager
16893         // so we just set the id rather than calling the setter.
16894         this.handleElId = id;
16895
16896         // the linked element is the element that gets dragged by default
16897         this.setDragElId(id);
16898
16899         // by default, clicked anchors will not start drag operations.
16900         this.invalidHandleTypes = { A: "A" };
16901         this.invalidHandleIds = {};
16902         this.invalidHandleClasses = [];
16903
16904         this.applyConfig();
16905
16906         this.handleOnAvailable();
16907     },
16908
16909     /**
16910      * Applies the configuration parameters that were passed into the constructor.
16911      * This is supposed to happen at each level through the inheritance chain.  So
16912      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16913      * DragDrop in order to get all of the parameters that are available in
16914      * each object.
16915      * @method applyConfig
16916      */
16917     applyConfig: function() {
16918
16919         // configurable properties:
16920         //    padding, isTarget, maintainOffset, primaryButtonOnly
16921         this.padding           = this.config.padding || [0, 0, 0, 0];
16922         this.isTarget          = (this.config.isTarget !== false);
16923         this.maintainOffset    = (this.config.maintainOffset);
16924         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16925
16926     },
16927
16928     /**
16929      * Executed when the linked element is available
16930      * @method handleOnAvailable
16931      * @private
16932      */
16933     handleOnAvailable: function() {
16934         this.available = true;
16935         this.resetConstraints();
16936         this.onAvailable();
16937     },
16938
16939      /**
16940      * Configures the padding for the target zone in px.  Effectively expands
16941      * (or reduces) the virtual object size for targeting calculations.
16942      * Supports css-style shorthand; if only one parameter is passed, all sides
16943      * will have that padding, and if only two are passed, the top and bottom
16944      * will have the first param, the left and right the second.
16945      * @method setPadding
16946      * @param {int} iTop    Top pad
16947      * @param {int} iRight  Right pad
16948      * @param {int} iBot    Bot pad
16949      * @param {int} iLeft   Left pad
16950      */
16951     setPadding: function(iTop, iRight, iBot, iLeft) {
16952         // this.padding = [iLeft, iRight, iTop, iBot];
16953         if (!iRight && 0 !== iRight) {
16954             this.padding = [iTop, iTop, iTop, iTop];
16955         } else if (!iBot && 0 !== iBot) {
16956             this.padding = [iTop, iRight, iTop, iRight];
16957         } else {
16958             this.padding = [iTop, iRight, iBot, iLeft];
16959         }
16960     },
16961
16962     /**
16963      * Stores the initial placement of the linked element.
16964      * @method setInitialPosition
16965      * @param {int} diffX   the X offset, default 0
16966      * @param {int} diffY   the Y offset, default 0
16967      */
16968     setInitPosition: function(diffX, diffY) {
16969         var el = this.getEl();
16970
16971         if (!this.DDM.verifyEl(el)) {
16972             return;
16973         }
16974
16975         var dx = diffX || 0;
16976         var dy = diffY || 0;
16977
16978         var p = Dom.getXY( el );
16979
16980         this.initPageX = p[0] - dx;
16981         this.initPageY = p[1] - dy;
16982
16983         this.lastPageX = p[0];
16984         this.lastPageY = p[1];
16985
16986
16987         this.setStartPosition(p);
16988     },
16989
16990     /**
16991      * Sets the start position of the element.  This is set when the obj
16992      * is initialized, the reset when a drag is started.
16993      * @method setStartPosition
16994      * @param pos current position (from previous lookup)
16995      * @private
16996      */
16997     setStartPosition: function(pos) {
16998         var p = pos || Dom.getXY( this.getEl() );
16999         this.deltaSetXY = null;
17000
17001         this.startPageX = p[0];
17002         this.startPageY = p[1];
17003     },
17004
17005     /**
17006      * Add this instance to a group of related drag/drop objects.  All
17007      * instances belong to at least one group, and can belong to as many
17008      * groups as needed.
17009      * @method addToGroup
17010      * @param sGroup {string} the name of the group
17011      */
17012     addToGroup: function(sGroup) {
17013         this.groups[sGroup] = true;
17014         this.DDM.regDragDrop(this, sGroup);
17015     },
17016
17017     /**
17018      * Remove's this instance from the supplied interaction group
17019      * @method removeFromGroup
17020      * @param {string}  sGroup  The group to drop
17021      */
17022     removeFromGroup: function(sGroup) {
17023         if (this.groups[sGroup]) {
17024             delete this.groups[sGroup];
17025         }
17026
17027         this.DDM.removeDDFromGroup(this, sGroup);
17028     },
17029
17030     /**
17031      * Allows you to specify that an element other than the linked element
17032      * will be moved with the cursor during a drag
17033      * @method setDragElId
17034      * @param id {string} the id of the element that will be used to initiate the drag
17035      */
17036     setDragElId: function(id) {
17037         this.dragElId = id;
17038     },
17039
17040     /**
17041      * Allows you to specify a child of the linked element that should be
17042      * used to initiate the drag operation.  An example of this would be if
17043      * you have a content div with text and links.  Clicking anywhere in the
17044      * content area would normally start the drag operation.  Use this method
17045      * to specify that an element inside of the content div is the element
17046      * that starts the drag operation.
17047      * @method setHandleElId
17048      * @param id {string} the id of the element that will be used to
17049      * initiate the drag.
17050      */
17051     setHandleElId: function(id) {
17052         if (typeof id !== "string") {
17053             id = Roo.id(id);
17054         }
17055         this.handleElId = id;
17056         this.DDM.regHandle(this.id, id);
17057     },
17058
17059     /**
17060      * Allows you to set an element outside of the linked element as a drag
17061      * handle
17062      * @method setOuterHandleElId
17063      * @param id the id of the element that will be used to initiate the drag
17064      */
17065     setOuterHandleElId: function(id) {
17066         if (typeof id !== "string") {
17067             id = Roo.id(id);
17068         }
17069         Event.on(id, "mousedown",
17070                 this.handleMouseDown, this);
17071         this.setHandleElId(id);
17072
17073         this.hasOuterHandles = true;
17074     },
17075
17076     /**
17077      * Remove all drag and drop hooks for this element
17078      * @method unreg
17079      */
17080     unreg: function() {
17081         Event.un(this.id, "mousedown",
17082                 this.handleMouseDown);
17083         Event.un(this.id, "touchstart",
17084                 this.handleMouseDown);
17085         this._domRef = null;
17086         this.DDM._remove(this);
17087     },
17088
17089     destroy : function(){
17090         this.unreg();
17091     },
17092
17093     /**
17094      * Returns true if this instance is locked, or the drag drop mgr is locked
17095      * (meaning that all drag/drop is disabled on the page.)
17096      * @method isLocked
17097      * @return {boolean} true if this obj or all drag/drop is locked, else
17098      * false
17099      */
17100     isLocked: function() {
17101         return (this.DDM.isLocked() || this.locked);
17102     },
17103
17104     /**
17105      * Fired when this object is clicked
17106      * @method handleMouseDown
17107      * @param {Event} e
17108      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17109      * @private
17110      */
17111     handleMouseDown: function(e, oDD){
17112      
17113         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17114             //Roo.log('not touch/ button !=0');
17115             return;
17116         }
17117         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17118             return; // double touch..
17119         }
17120         
17121
17122         if (this.isLocked()) {
17123             //Roo.log('locked');
17124             return;
17125         }
17126
17127         this.DDM.refreshCache(this.groups);
17128 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17129         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17130         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17131             //Roo.log('no outer handes or not over target');
17132                 // do nothing.
17133         } else {
17134 //            Roo.log('check validator');
17135             if (this.clickValidator(e)) {
17136 //                Roo.log('validate success');
17137                 // set the initial element position
17138                 this.setStartPosition();
17139
17140
17141                 this.b4MouseDown(e);
17142                 this.onMouseDown(e);
17143
17144                 this.DDM.handleMouseDown(e, this);
17145
17146                 this.DDM.stopEvent(e);
17147             } else {
17148
17149
17150             }
17151         }
17152     },
17153
17154     clickValidator: function(e) {
17155         var target = e.getTarget();
17156         return ( this.isValidHandleChild(target) &&
17157                     (this.id == this.handleElId ||
17158                         this.DDM.handleWasClicked(target, this.id)) );
17159     },
17160
17161     /**
17162      * Allows you to specify a tag name that should not start a drag operation
17163      * when clicked.  This is designed to facilitate embedding links within a
17164      * drag handle that do something other than start the drag.
17165      * @method addInvalidHandleType
17166      * @param {string} tagName the type of element to exclude
17167      */
17168     addInvalidHandleType: function(tagName) {
17169         var type = tagName.toUpperCase();
17170         this.invalidHandleTypes[type] = type;
17171     },
17172
17173     /**
17174      * Lets you to specify an element id for a child of a drag handle
17175      * that should not initiate a drag
17176      * @method addInvalidHandleId
17177      * @param {string} id the element id of the element you wish to ignore
17178      */
17179     addInvalidHandleId: function(id) {
17180         if (typeof id !== "string") {
17181             id = Roo.id(id);
17182         }
17183         this.invalidHandleIds[id] = id;
17184     },
17185
17186     /**
17187      * Lets you specify a css class of elements that will not initiate a drag
17188      * @method addInvalidHandleClass
17189      * @param {string} cssClass the class of the elements you wish to ignore
17190      */
17191     addInvalidHandleClass: function(cssClass) {
17192         this.invalidHandleClasses.push(cssClass);
17193     },
17194
17195     /**
17196      * Unsets an excluded tag name set by addInvalidHandleType
17197      * @method removeInvalidHandleType
17198      * @param {string} tagName the type of element to unexclude
17199      */
17200     removeInvalidHandleType: function(tagName) {
17201         var type = tagName.toUpperCase();
17202         // this.invalidHandleTypes[type] = null;
17203         delete this.invalidHandleTypes[type];
17204     },
17205
17206     /**
17207      * Unsets an invalid handle id
17208      * @method removeInvalidHandleId
17209      * @param {string} id the id of the element to re-enable
17210      */
17211     removeInvalidHandleId: function(id) {
17212         if (typeof id !== "string") {
17213             id = Roo.id(id);
17214         }
17215         delete this.invalidHandleIds[id];
17216     },
17217
17218     /**
17219      * Unsets an invalid css class
17220      * @method removeInvalidHandleClass
17221      * @param {string} cssClass the class of the element(s) you wish to
17222      * re-enable
17223      */
17224     removeInvalidHandleClass: function(cssClass) {
17225         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17226             if (this.invalidHandleClasses[i] == cssClass) {
17227                 delete this.invalidHandleClasses[i];
17228             }
17229         }
17230     },
17231
17232     /**
17233      * Checks the tag exclusion list to see if this click should be ignored
17234      * @method isValidHandleChild
17235      * @param {HTMLElement} node the HTMLElement to evaluate
17236      * @return {boolean} true if this is a valid tag type, false if not
17237      */
17238     isValidHandleChild: function(node) {
17239
17240         var valid = true;
17241         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17242         var nodeName;
17243         try {
17244             nodeName = node.nodeName.toUpperCase();
17245         } catch(e) {
17246             nodeName = node.nodeName;
17247         }
17248         valid = valid && !this.invalidHandleTypes[nodeName];
17249         valid = valid && !this.invalidHandleIds[node.id];
17250
17251         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17252             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17253         }
17254
17255
17256         return valid;
17257
17258     },
17259
17260     /**
17261      * Create the array of horizontal tick marks if an interval was specified
17262      * in setXConstraint().
17263      * @method setXTicks
17264      * @private
17265      */
17266     setXTicks: function(iStartX, iTickSize) {
17267         this.xTicks = [];
17268         this.xTickSize = iTickSize;
17269
17270         var tickMap = {};
17271
17272         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17273             if (!tickMap[i]) {
17274                 this.xTicks[this.xTicks.length] = i;
17275                 tickMap[i] = true;
17276             }
17277         }
17278
17279         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17280             if (!tickMap[i]) {
17281                 this.xTicks[this.xTicks.length] = i;
17282                 tickMap[i] = true;
17283             }
17284         }
17285
17286         this.xTicks.sort(this.DDM.numericSort) ;
17287     },
17288
17289     /**
17290      * Create the array of vertical tick marks if an interval was specified in
17291      * setYConstraint().
17292      * @method setYTicks
17293      * @private
17294      */
17295     setYTicks: function(iStartY, iTickSize) {
17296         this.yTicks = [];
17297         this.yTickSize = iTickSize;
17298
17299         var tickMap = {};
17300
17301         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17302             if (!tickMap[i]) {
17303                 this.yTicks[this.yTicks.length] = i;
17304                 tickMap[i] = true;
17305             }
17306         }
17307
17308         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17309             if (!tickMap[i]) {
17310                 this.yTicks[this.yTicks.length] = i;
17311                 tickMap[i] = true;
17312             }
17313         }
17314
17315         this.yTicks.sort(this.DDM.numericSort) ;
17316     },
17317
17318     /**
17319      * By default, the element can be dragged any place on the screen.  Use
17320      * this method to limit the horizontal travel of the element.  Pass in
17321      * 0,0 for the parameters if you want to lock the drag to the y axis.
17322      * @method setXConstraint
17323      * @param {int} iLeft the number of pixels the element can move to the left
17324      * @param {int} iRight the number of pixels the element can move to the
17325      * right
17326      * @param {int} iTickSize optional parameter for specifying that the
17327      * element
17328      * should move iTickSize pixels at a time.
17329      */
17330     setXConstraint: function(iLeft, iRight, iTickSize) {
17331         this.leftConstraint = iLeft;
17332         this.rightConstraint = iRight;
17333
17334         this.minX = this.initPageX - iLeft;
17335         this.maxX = this.initPageX + iRight;
17336         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17337
17338         this.constrainX = true;
17339     },
17340
17341     /**
17342      * Clears any constraints applied to this instance.  Also clears ticks
17343      * since they can't exist independent of a constraint at this time.
17344      * @method clearConstraints
17345      */
17346     clearConstraints: function() {
17347         this.constrainX = false;
17348         this.constrainY = false;
17349         this.clearTicks();
17350     },
17351
17352     /**
17353      * Clears any tick interval defined for this instance
17354      * @method clearTicks
17355      */
17356     clearTicks: function() {
17357         this.xTicks = null;
17358         this.yTicks = null;
17359         this.xTickSize = 0;
17360         this.yTickSize = 0;
17361     },
17362
17363     /**
17364      * By default, the element can be dragged any place on the screen.  Set
17365      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17366      * parameters if you want to lock the drag to the x axis.
17367      * @method setYConstraint
17368      * @param {int} iUp the number of pixels the element can move up
17369      * @param {int} iDown the number of pixels the element can move down
17370      * @param {int} iTickSize optional parameter for specifying that the
17371      * element should move iTickSize pixels at a time.
17372      */
17373     setYConstraint: function(iUp, iDown, iTickSize) {
17374         this.topConstraint = iUp;
17375         this.bottomConstraint = iDown;
17376
17377         this.minY = this.initPageY - iUp;
17378         this.maxY = this.initPageY + iDown;
17379         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17380
17381         this.constrainY = true;
17382
17383     },
17384
17385     /**
17386      * resetConstraints must be called if you manually reposition a dd element.
17387      * @method resetConstraints
17388      * @param {boolean} maintainOffset
17389      */
17390     resetConstraints: function() {
17391
17392
17393         // Maintain offsets if necessary
17394         if (this.initPageX || this.initPageX === 0) {
17395             // figure out how much this thing has moved
17396             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17397             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17398
17399             this.setInitPosition(dx, dy);
17400
17401         // This is the first time we have detected the element's position
17402         } else {
17403             this.setInitPosition();
17404         }
17405
17406         if (this.constrainX) {
17407             this.setXConstraint( this.leftConstraint,
17408                                  this.rightConstraint,
17409                                  this.xTickSize        );
17410         }
17411
17412         if (this.constrainY) {
17413             this.setYConstraint( this.topConstraint,
17414                                  this.bottomConstraint,
17415                                  this.yTickSize         );
17416         }
17417     },
17418
17419     /**
17420      * Normally the drag element is moved pixel by pixel, but we can specify
17421      * that it move a number of pixels at a time.  This method resolves the
17422      * location when we have it set up like this.
17423      * @method getTick
17424      * @param {int} val where we want to place the object
17425      * @param {int[]} tickArray sorted array of valid points
17426      * @return {int} the closest tick
17427      * @private
17428      */
17429     getTick: function(val, tickArray) {
17430
17431         if (!tickArray) {
17432             // If tick interval is not defined, it is effectively 1 pixel,
17433             // so we return the value passed to us.
17434             return val;
17435         } else if (tickArray[0] >= val) {
17436             // The value is lower than the first tick, so we return the first
17437             // tick.
17438             return tickArray[0];
17439         } else {
17440             for (var i=0, len=tickArray.length; i<len; ++i) {
17441                 var next = i + 1;
17442                 if (tickArray[next] && tickArray[next] >= val) {
17443                     var diff1 = val - tickArray[i];
17444                     var diff2 = tickArray[next] - val;
17445                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17446                 }
17447             }
17448
17449             // The value is larger than the last tick, so we return the last
17450             // tick.
17451             return tickArray[tickArray.length - 1];
17452         }
17453     },
17454
17455     /**
17456      * toString method
17457      * @method toString
17458      * @return {string} string representation of the dd obj
17459      */
17460     toString: function() {
17461         return ("DragDrop " + this.id);
17462     }
17463
17464 });
17465
17466 })();
17467 /*
17468  * Based on:
17469  * Ext JS Library 1.1.1
17470  * Copyright(c) 2006-2007, Ext JS, LLC.
17471  *
17472  * Originally Released Under LGPL - original licence link has changed is not relivant.
17473  *
17474  * Fork - LGPL
17475  * <script type="text/javascript">
17476  */
17477
17478
17479 /**
17480  * The drag and drop utility provides a framework for building drag and drop
17481  * applications.  In addition to enabling drag and drop for specific elements,
17482  * the drag and drop elements are tracked by the manager class, and the
17483  * interactions between the various elements are tracked during the drag and
17484  * the implementing code is notified about these important moments.
17485  */
17486
17487 // Only load the library once.  Rewriting the manager class would orphan
17488 // existing drag and drop instances.
17489 if (!Roo.dd.DragDropMgr) {
17490
17491 /**
17492  * @class Roo.dd.DragDropMgr
17493  * DragDropMgr is a singleton that tracks the element interaction for
17494  * all DragDrop items in the window.  Generally, you will not call
17495  * this class directly, but it does have helper methods that could
17496  * be useful in your DragDrop implementations.
17497  * @singleton
17498  */
17499 Roo.dd.DragDropMgr = function() {
17500
17501     var Event = Roo.EventManager;
17502
17503     return {
17504
17505         /**
17506          * Two dimensional Array of registered DragDrop objects.  The first
17507          * dimension is the DragDrop item group, the second the DragDrop
17508          * object.
17509          * @property ids
17510          * @type {string: string}
17511          * @private
17512          * @static
17513          */
17514         ids: {},
17515
17516         /**
17517          * Array of element ids defined as drag handles.  Used to determine
17518          * if the element that generated the mousedown event is actually the
17519          * handle and not the html element itself.
17520          * @property handleIds
17521          * @type {string: string}
17522          * @private
17523          * @static
17524          */
17525         handleIds: {},
17526
17527         /**
17528          * the DragDrop object that is currently being dragged
17529          * @property dragCurrent
17530          * @type DragDrop
17531          * @private
17532          * @static
17533          **/
17534         dragCurrent: null,
17535
17536         /**
17537          * the DragDrop object(s) that are being hovered over
17538          * @property dragOvers
17539          * @type Array
17540          * @private
17541          * @static
17542          */
17543         dragOvers: {},
17544
17545         /**
17546          * the X distance between the cursor and the object being dragged
17547          * @property deltaX
17548          * @type int
17549          * @private
17550          * @static
17551          */
17552         deltaX: 0,
17553
17554         /**
17555          * the Y distance between the cursor and the object being dragged
17556          * @property deltaY
17557          * @type int
17558          * @private
17559          * @static
17560          */
17561         deltaY: 0,
17562
17563         /**
17564          * Flag to determine if we should prevent the default behavior of the
17565          * events we define. By default this is true, but this can be set to
17566          * false if you need the default behavior (not recommended)
17567          * @property preventDefault
17568          * @type boolean
17569          * @static
17570          */
17571         preventDefault: true,
17572
17573         /**
17574          * Flag to determine if we should stop the propagation of the events
17575          * we generate. This is true by default but you may want to set it to
17576          * false if the html element contains other features that require the
17577          * mouse click.
17578          * @property stopPropagation
17579          * @type boolean
17580          * @static
17581          */
17582         stopPropagation: true,
17583
17584         /**
17585          * Internal flag that is set to true when drag and drop has been
17586          * intialized
17587          * @property initialized
17588          * @private
17589          * @static
17590          */
17591         initalized: false,
17592
17593         /**
17594          * All drag and drop can be disabled.
17595          * @property locked
17596          * @private
17597          * @static
17598          */
17599         locked: false,
17600
17601         /**
17602          * Called the first time an element is registered.
17603          * @method init
17604          * @private
17605          * @static
17606          */
17607         init: function() {
17608             this.initialized = true;
17609         },
17610
17611         /**
17612          * In point mode, drag and drop interaction is defined by the
17613          * location of the cursor during the drag/drop
17614          * @property POINT
17615          * @type int
17616          * @static
17617          */
17618         POINT: 0,
17619
17620         /**
17621          * In intersect mode, drag and drop interactio nis defined by the
17622          * overlap of two or more drag and drop objects.
17623          * @property INTERSECT
17624          * @type int
17625          * @static
17626          */
17627         INTERSECT: 1,
17628
17629         /**
17630          * The current drag and drop mode.  Default: POINT
17631          * @property mode
17632          * @type int
17633          * @static
17634          */
17635         mode: 0,
17636
17637         /**
17638          * Runs method on all drag and drop objects
17639          * @method _execOnAll
17640          * @private
17641          * @static
17642          */
17643         _execOnAll: function(sMethod, args) {
17644             for (var i in this.ids) {
17645                 for (var j in this.ids[i]) {
17646                     var oDD = this.ids[i][j];
17647                     if (! this.isTypeOfDD(oDD)) {
17648                         continue;
17649                     }
17650                     oDD[sMethod].apply(oDD, args);
17651                 }
17652             }
17653         },
17654
17655         /**
17656          * Drag and drop initialization.  Sets up the global event handlers
17657          * @method _onLoad
17658          * @private
17659          * @static
17660          */
17661         _onLoad: function() {
17662
17663             this.init();
17664
17665             if (!Roo.isTouch) {
17666                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17667                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17668             }
17669             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17670             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17671             
17672             Event.on(window,   "unload",    this._onUnload, this, true);
17673             Event.on(window,   "resize",    this._onResize, this, true);
17674             // Event.on(window,   "mouseout",    this._test);
17675
17676         },
17677
17678         /**
17679          * Reset constraints on all drag and drop objs
17680          * @method _onResize
17681          * @private
17682          * @static
17683          */
17684         _onResize: function(e) {
17685             this._execOnAll("resetConstraints", []);
17686         },
17687
17688         /**
17689          * Lock all drag and drop functionality
17690          * @method lock
17691          * @static
17692          */
17693         lock: function() { this.locked = true; },
17694
17695         /**
17696          * Unlock all drag and drop functionality
17697          * @method unlock
17698          * @static
17699          */
17700         unlock: function() { this.locked = false; },
17701
17702         /**
17703          * Is drag and drop locked?
17704          * @method isLocked
17705          * @return {boolean} True if drag and drop is locked, false otherwise.
17706          * @static
17707          */
17708         isLocked: function() { return this.locked; },
17709
17710         /**
17711          * Location cache that is set for all drag drop objects when a drag is
17712          * initiated, cleared when the drag is finished.
17713          * @property locationCache
17714          * @private
17715          * @static
17716          */
17717         locationCache: {},
17718
17719         /**
17720          * Set useCache to false if you want to force object the lookup of each
17721          * drag and drop linked element constantly during a drag.
17722          * @property useCache
17723          * @type boolean
17724          * @static
17725          */
17726         useCache: true,
17727
17728         /**
17729          * The number of pixels that the mouse needs to move after the
17730          * mousedown before the drag is initiated.  Default=3;
17731          * @property clickPixelThresh
17732          * @type int
17733          * @static
17734          */
17735         clickPixelThresh: 3,
17736
17737         /**
17738          * The number of milliseconds after the mousedown event to initiate the
17739          * drag if we don't get a mouseup event. Default=1000
17740          * @property clickTimeThresh
17741          * @type int
17742          * @static
17743          */
17744         clickTimeThresh: 350,
17745
17746         /**
17747          * Flag that indicates that either the drag pixel threshold or the
17748          * mousdown time threshold has been met
17749          * @property dragThreshMet
17750          * @type boolean
17751          * @private
17752          * @static
17753          */
17754         dragThreshMet: false,
17755
17756         /**
17757          * Timeout used for the click time threshold
17758          * @property clickTimeout
17759          * @type Object
17760          * @private
17761          * @static
17762          */
17763         clickTimeout: null,
17764
17765         /**
17766          * The X position of the mousedown event stored for later use when a
17767          * drag threshold is met.
17768          * @property startX
17769          * @type int
17770          * @private
17771          * @static
17772          */
17773         startX: 0,
17774
17775         /**
17776          * The Y position of the mousedown event stored for later use when a
17777          * drag threshold is met.
17778          * @property startY
17779          * @type int
17780          * @private
17781          * @static
17782          */
17783         startY: 0,
17784
17785         /**
17786          * Each DragDrop instance must be registered with the DragDropMgr.
17787          * This is executed in DragDrop.init()
17788          * @method regDragDrop
17789          * @param {DragDrop} oDD the DragDrop object to register
17790          * @param {String} sGroup the name of the group this element belongs to
17791          * @static
17792          */
17793         regDragDrop: function(oDD, sGroup) {
17794             if (!this.initialized) { this.init(); }
17795
17796             if (!this.ids[sGroup]) {
17797                 this.ids[sGroup] = {};
17798             }
17799             this.ids[sGroup][oDD.id] = oDD;
17800         },
17801
17802         /**
17803          * Removes the supplied dd instance from the supplied group. Executed
17804          * by DragDrop.removeFromGroup, so don't call this function directly.
17805          * @method removeDDFromGroup
17806          * @private
17807          * @static
17808          */
17809         removeDDFromGroup: function(oDD, sGroup) {
17810             if (!this.ids[sGroup]) {
17811                 this.ids[sGroup] = {};
17812             }
17813
17814             var obj = this.ids[sGroup];
17815             if (obj && obj[oDD.id]) {
17816                 delete obj[oDD.id];
17817             }
17818         },
17819
17820         /**
17821          * Unregisters a drag and drop item.  This is executed in
17822          * DragDrop.unreg, use that method instead of calling this directly.
17823          * @method _remove
17824          * @private
17825          * @static
17826          */
17827         _remove: function(oDD) {
17828             for (var g in oDD.groups) {
17829                 if (g && this.ids[g][oDD.id]) {
17830                     delete this.ids[g][oDD.id];
17831                 }
17832             }
17833             delete this.handleIds[oDD.id];
17834         },
17835
17836         /**
17837          * Each DragDrop handle element must be registered.  This is done
17838          * automatically when executing DragDrop.setHandleElId()
17839          * @method regHandle
17840          * @param {String} sDDId the DragDrop id this element is a handle for
17841          * @param {String} sHandleId the id of the element that is the drag
17842          * handle
17843          * @static
17844          */
17845         regHandle: function(sDDId, sHandleId) {
17846             if (!this.handleIds[sDDId]) {
17847                 this.handleIds[sDDId] = {};
17848             }
17849             this.handleIds[sDDId][sHandleId] = sHandleId;
17850         },
17851
17852         /**
17853          * Utility function to determine if a given element has been
17854          * registered as a drag drop item.
17855          * @method isDragDrop
17856          * @param {String} id the element id to check
17857          * @return {boolean} true if this element is a DragDrop item,
17858          * false otherwise
17859          * @static
17860          */
17861         isDragDrop: function(id) {
17862             return ( this.getDDById(id) ) ? true : false;
17863         },
17864
17865         /**
17866          * Returns the drag and drop instances that are in all groups the
17867          * passed in instance belongs to.
17868          * @method getRelated
17869          * @param {DragDrop} p_oDD the obj to get related data for
17870          * @param {boolean} bTargetsOnly if true, only return targetable objs
17871          * @return {DragDrop[]} the related instances
17872          * @static
17873          */
17874         getRelated: function(p_oDD, bTargetsOnly) {
17875             var oDDs = [];
17876             for (var i in p_oDD.groups) {
17877                 for (j in this.ids[i]) {
17878                     var dd = this.ids[i][j];
17879                     if (! this.isTypeOfDD(dd)) {
17880                         continue;
17881                     }
17882                     if (!bTargetsOnly || dd.isTarget) {
17883                         oDDs[oDDs.length] = dd;
17884                     }
17885                 }
17886             }
17887
17888             return oDDs;
17889         },
17890
17891         /**
17892          * Returns true if the specified dd target is a legal target for
17893          * the specifice drag obj
17894          * @method isLegalTarget
17895          * @param {DragDrop} the drag obj
17896          * @param {DragDrop} the target
17897          * @return {boolean} true if the target is a legal target for the
17898          * dd obj
17899          * @static
17900          */
17901         isLegalTarget: function (oDD, oTargetDD) {
17902             var targets = this.getRelated(oDD, true);
17903             for (var i=0, len=targets.length;i<len;++i) {
17904                 if (targets[i].id == oTargetDD.id) {
17905                     return true;
17906                 }
17907             }
17908
17909             return false;
17910         },
17911
17912         /**
17913          * My goal is to be able to transparently determine if an object is
17914          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17915          * returns "object", oDD.constructor.toString() always returns
17916          * "DragDrop" and not the name of the subclass.  So for now it just
17917          * evaluates a well-known variable in DragDrop.
17918          * @method isTypeOfDD
17919          * @param {Object} the object to evaluate
17920          * @return {boolean} true if typeof oDD = DragDrop
17921          * @static
17922          */
17923         isTypeOfDD: function (oDD) {
17924             return (oDD && oDD.__ygDragDrop);
17925         },
17926
17927         /**
17928          * Utility function to determine if a given element has been
17929          * registered as a drag drop handle for the given Drag Drop object.
17930          * @method isHandle
17931          * @param {String} id the element id to check
17932          * @return {boolean} true if this element is a DragDrop handle, false
17933          * otherwise
17934          * @static
17935          */
17936         isHandle: function(sDDId, sHandleId) {
17937             return ( this.handleIds[sDDId] &&
17938                             this.handleIds[sDDId][sHandleId] );
17939         },
17940
17941         /**
17942          * Returns the DragDrop instance for a given id
17943          * @method getDDById
17944          * @param {String} id the id of the DragDrop object
17945          * @return {DragDrop} the drag drop object, null if it is not found
17946          * @static
17947          */
17948         getDDById: function(id) {
17949             for (var i in this.ids) {
17950                 if (this.ids[i][id]) {
17951                     return this.ids[i][id];
17952                 }
17953             }
17954             return null;
17955         },
17956
17957         /**
17958          * Fired after a registered DragDrop object gets the mousedown event.
17959          * Sets up the events required to track the object being dragged
17960          * @method handleMouseDown
17961          * @param {Event} e the event
17962          * @param oDD the DragDrop object being dragged
17963          * @private
17964          * @static
17965          */
17966         handleMouseDown: function(e, oDD) {
17967             if(Roo.QuickTips){
17968                 Roo.QuickTips.disable();
17969             }
17970             this.currentTarget = e.getTarget();
17971
17972             this.dragCurrent = oDD;
17973
17974             var el = oDD.getEl();
17975
17976             // track start position
17977             this.startX = e.getPageX();
17978             this.startY = e.getPageY();
17979
17980             this.deltaX = this.startX - el.offsetLeft;
17981             this.deltaY = this.startY - el.offsetTop;
17982
17983             this.dragThreshMet = false;
17984
17985             this.clickTimeout = setTimeout(
17986                     function() {
17987                         var DDM = Roo.dd.DDM;
17988                         DDM.startDrag(DDM.startX, DDM.startY);
17989                     },
17990                     this.clickTimeThresh );
17991         },
17992
17993         /**
17994          * Fired when either the drag pixel threshol or the mousedown hold
17995          * time threshold has been met.
17996          * @method startDrag
17997          * @param x {int} the X position of the original mousedown
17998          * @param y {int} the Y position of the original mousedown
17999          * @static
18000          */
18001         startDrag: function(x, y) {
18002             clearTimeout(this.clickTimeout);
18003             if (this.dragCurrent) {
18004                 this.dragCurrent.b4StartDrag(x, y);
18005                 this.dragCurrent.startDrag(x, y);
18006             }
18007             this.dragThreshMet = true;
18008         },
18009
18010         /**
18011          * Internal function to handle the mouseup event.  Will be invoked
18012          * from the context of the document.
18013          * @method handleMouseUp
18014          * @param {Event} e the event
18015          * @private
18016          * @static
18017          */
18018         handleMouseUp: function(e) {
18019
18020             if(Roo.QuickTips){
18021                 Roo.QuickTips.enable();
18022             }
18023             if (! this.dragCurrent) {
18024                 return;
18025             }
18026
18027             clearTimeout(this.clickTimeout);
18028
18029             if (this.dragThreshMet) {
18030                 this.fireEvents(e, true);
18031             } else {
18032             }
18033
18034             this.stopDrag(e);
18035
18036             this.stopEvent(e);
18037         },
18038
18039         /**
18040          * Utility to stop event propagation and event default, if these
18041          * features are turned on.
18042          * @method stopEvent
18043          * @param {Event} e the event as returned by this.getEvent()
18044          * @static
18045          */
18046         stopEvent: function(e){
18047             if(this.stopPropagation) {
18048                 e.stopPropagation();
18049             }
18050
18051             if (this.preventDefault) {
18052                 e.preventDefault();
18053             }
18054         },
18055
18056         /**
18057          * Internal function to clean up event handlers after the drag
18058          * operation is complete
18059          * @method stopDrag
18060          * @param {Event} e the event
18061          * @private
18062          * @static
18063          */
18064         stopDrag: function(e) {
18065             // Fire the drag end event for the item that was dragged
18066             if (this.dragCurrent) {
18067                 if (this.dragThreshMet) {
18068                     this.dragCurrent.b4EndDrag(e);
18069                     this.dragCurrent.endDrag(e);
18070                 }
18071
18072                 this.dragCurrent.onMouseUp(e);
18073             }
18074
18075             this.dragCurrent = null;
18076             this.dragOvers = {};
18077         },
18078
18079         /**
18080          * Internal function to handle the mousemove event.  Will be invoked
18081          * from the context of the html element.
18082          *
18083          * @TODO figure out what we can do about mouse events lost when the
18084          * user drags objects beyond the window boundary.  Currently we can
18085          * detect this in internet explorer by verifying that the mouse is
18086          * down during the mousemove event.  Firefox doesn't give us the
18087          * button state on the mousemove event.
18088          * @method handleMouseMove
18089          * @param {Event} e the event
18090          * @private
18091          * @static
18092          */
18093         handleMouseMove: function(e) {
18094             if (! this.dragCurrent) {
18095                 return true;
18096             }
18097
18098             // var button = e.which || e.button;
18099
18100             // check for IE mouseup outside of page boundary
18101             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18102                 this.stopEvent(e);
18103                 return this.handleMouseUp(e);
18104             }
18105
18106             if (!this.dragThreshMet) {
18107                 var diffX = Math.abs(this.startX - e.getPageX());
18108                 var diffY = Math.abs(this.startY - e.getPageY());
18109                 if (diffX > this.clickPixelThresh ||
18110                             diffY > this.clickPixelThresh) {
18111                     this.startDrag(this.startX, this.startY);
18112                 }
18113             }
18114
18115             if (this.dragThreshMet) {
18116                 this.dragCurrent.b4Drag(e);
18117                 this.dragCurrent.onDrag(e);
18118                 if(!this.dragCurrent.moveOnly){
18119                     this.fireEvents(e, false);
18120                 }
18121             }
18122
18123             this.stopEvent(e);
18124
18125             return true;
18126         },
18127
18128         /**
18129          * Iterates over all of the DragDrop elements to find ones we are
18130          * hovering over or dropping on
18131          * @method fireEvents
18132          * @param {Event} e the event
18133          * @param {boolean} isDrop is this a drop op or a mouseover op?
18134          * @private
18135          * @static
18136          */
18137         fireEvents: function(e, isDrop) {
18138             var dc = this.dragCurrent;
18139
18140             // If the user did the mouse up outside of the window, we could
18141             // get here even though we have ended the drag.
18142             if (!dc || dc.isLocked()) {
18143                 return;
18144             }
18145
18146             var pt = e.getPoint();
18147
18148             // cache the previous dragOver array
18149             var oldOvers = [];
18150
18151             var outEvts   = [];
18152             var overEvts  = [];
18153             var dropEvts  = [];
18154             var enterEvts = [];
18155
18156             // Check to see if the object(s) we were hovering over is no longer
18157             // being hovered over so we can fire the onDragOut event
18158             for (var i in this.dragOvers) {
18159
18160                 var ddo = this.dragOvers[i];
18161
18162                 if (! this.isTypeOfDD(ddo)) {
18163                     continue;
18164                 }
18165
18166                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18167                     outEvts.push( ddo );
18168                 }
18169
18170                 oldOvers[i] = true;
18171                 delete this.dragOvers[i];
18172             }
18173
18174             for (var sGroup in dc.groups) {
18175
18176                 if ("string" != typeof sGroup) {
18177                     continue;
18178                 }
18179
18180                 for (i in this.ids[sGroup]) {
18181                     var oDD = this.ids[sGroup][i];
18182                     if (! this.isTypeOfDD(oDD)) {
18183                         continue;
18184                     }
18185
18186                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18187                         if (this.isOverTarget(pt, oDD, this.mode)) {
18188                             // look for drop interactions
18189                             if (isDrop) {
18190                                 dropEvts.push( oDD );
18191                             // look for drag enter and drag over interactions
18192                             } else {
18193
18194                                 // initial drag over: dragEnter fires
18195                                 if (!oldOvers[oDD.id]) {
18196                                     enterEvts.push( oDD );
18197                                 // subsequent drag overs: dragOver fires
18198                                 } else {
18199                                     overEvts.push( oDD );
18200                                 }
18201
18202                                 this.dragOvers[oDD.id] = oDD;
18203                             }
18204                         }
18205                     }
18206                 }
18207             }
18208
18209             if (this.mode) {
18210                 if (outEvts.length) {
18211                     dc.b4DragOut(e, outEvts);
18212                     dc.onDragOut(e, outEvts);
18213                 }
18214
18215                 if (enterEvts.length) {
18216                     dc.onDragEnter(e, enterEvts);
18217                 }
18218
18219                 if (overEvts.length) {
18220                     dc.b4DragOver(e, overEvts);
18221                     dc.onDragOver(e, overEvts);
18222                 }
18223
18224                 if (dropEvts.length) {
18225                     dc.b4DragDrop(e, dropEvts);
18226                     dc.onDragDrop(e, dropEvts);
18227                 }
18228
18229             } else {
18230                 // fire dragout events
18231                 var len = 0;
18232                 for (i=0, len=outEvts.length; i<len; ++i) {
18233                     dc.b4DragOut(e, outEvts[i].id);
18234                     dc.onDragOut(e, outEvts[i].id);
18235                 }
18236
18237                 // fire enter events
18238                 for (i=0,len=enterEvts.length; i<len; ++i) {
18239                     // dc.b4DragEnter(e, oDD.id);
18240                     dc.onDragEnter(e, enterEvts[i].id);
18241                 }
18242
18243                 // fire over events
18244                 for (i=0,len=overEvts.length; i<len; ++i) {
18245                     dc.b4DragOver(e, overEvts[i].id);
18246                     dc.onDragOver(e, overEvts[i].id);
18247                 }
18248
18249                 // fire drop events
18250                 for (i=0, len=dropEvts.length; i<len; ++i) {
18251                     dc.b4DragDrop(e, dropEvts[i].id);
18252                     dc.onDragDrop(e, dropEvts[i].id);
18253                 }
18254
18255             }
18256
18257             // notify about a drop that did not find a target
18258             if (isDrop && !dropEvts.length) {
18259                 dc.onInvalidDrop(e);
18260             }
18261
18262         },
18263
18264         /**
18265          * Helper function for getting the best match from the list of drag
18266          * and drop objects returned by the drag and drop events when we are
18267          * in INTERSECT mode.  It returns either the first object that the
18268          * cursor is over, or the object that has the greatest overlap with
18269          * the dragged element.
18270          * @method getBestMatch
18271          * @param  {DragDrop[]} dds The array of drag and drop objects
18272          * targeted
18273          * @return {DragDrop}       The best single match
18274          * @static
18275          */
18276         getBestMatch: function(dds) {
18277             var winner = null;
18278             // Return null if the input is not what we expect
18279             //if (!dds || !dds.length || dds.length == 0) {
18280                // winner = null;
18281             // If there is only one item, it wins
18282             //} else if (dds.length == 1) {
18283
18284             var len = dds.length;
18285
18286             if (len == 1) {
18287                 winner = dds[0];
18288             } else {
18289                 // Loop through the targeted items
18290                 for (var i=0; i<len; ++i) {
18291                     var dd = dds[i];
18292                     // If the cursor is over the object, it wins.  If the
18293                     // cursor is over multiple matches, the first one we come
18294                     // to wins.
18295                     if (dd.cursorIsOver) {
18296                         winner = dd;
18297                         break;
18298                     // Otherwise the object with the most overlap wins
18299                     } else {
18300                         if (!winner ||
18301                             winner.overlap.getArea() < dd.overlap.getArea()) {
18302                             winner = dd;
18303                         }
18304                     }
18305                 }
18306             }
18307
18308             return winner;
18309         },
18310
18311         /**
18312          * Refreshes the cache of the top-left and bottom-right points of the
18313          * drag and drop objects in the specified group(s).  This is in the
18314          * format that is stored in the drag and drop instance, so typical
18315          * usage is:
18316          * <code>
18317          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18318          * </code>
18319          * Alternatively:
18320          * <code>
18321          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18322          * </code>
18323          * @TODO this really should be an indexed array.  Alternatively this
18324          * method could accept both.
18325          * @method refreshCache
18326          * @param {Object} groups an associative array of groups to refresh
18327          * @static
18328          */
18329         refreshCache: function(groups) {
18330             for (var sGroup in groups) {
18331                 if ("string" != typeof sGroup) {
18332                     continue;
18333                 }
18334                 for (var i in this.ids[sGroup]) {
18335                     var oDD = this.ids[sGroup][i];
18336
18337                     if (this.isTypeOfDD(oDD)) {
18338                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18339                         var loc = this.getLocation(oDD);
18340                         if (loc) {
18341                             this.locationCache[oDD.id] = loc;
18342                         } else {
18343                             delete this.locationCache[oDD.id];
18344                             // this will unregister the drag and drop object if
18345                             // the element is not in a usable state
18346                             // oDD.unreg();
18347                         }
18348                     }
18349                 }
18350             }
18351         },
18352
18353         /**
18354          * This checks to make sure an element exists and is in the DOM.  The
18355          * main purpose is to handle cases where innerHTML is used to remove
18356          * drag and drop objects from the DOM.  IE provides an 'unspecified
18357          * error' when trying to access the offsetParent of such an element
18358          * @method verifyEl
18359          * @param {HTMLElement} el the element to check
18360          * @return {boolean} true if the element looks usable
18361          * @static
18362          */
18363         verifyEl: function(el) {
18364             if (el) {
18365                 var parent;
18366                 if(Roo.isIE){
18367                     try{
18368                         parent = el.offsetParent;
18369                     }catch(e){}
18370                 }else{
18371                     parent = el.offsetParent;
18372                 }
18373                 if (parent) {
18374                     return true;
18375                 }
18376             }
18377
18378             return false;
18379         },
18380
18381         /**
18382          * Returns a Region object containing the drag and drop element's position
18383          * and size, including the padding configured for it
18384          * @method getLocation
18385          * @param {DragDrop} oDD the drag and drop object to get the
18386          *                       location for
18387          * @return {Roo.lib.Region} a Region object representing the total area
18388          *                             the element occupies, including any padding
18389          *                             the instance is configured for.
18390          * @static
18391          */
18392         getLocation: function(oDD) {
18393             if (! this.isTypeOfDD(oDD)) {
18394                 return null;
18395             }
18396
18397             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18398
18399             try {
18400                 pos= Roo.lib.Dom.getXY(el);
18401             } catch (e) { }
18402
18403             if (!pos) {
18404                 return null;
18405             }
18406
18407             x1 = pos[0];
18408             x2 = x1 + el.offsetWidth;
18409             y1 = pos[1];
18410             y2 = y1 + el.offsetHeight;
18411
18412             t = y1 - oDD.padding[0];
18413             r = x2 + oDD.padding[1];
18414             b = y2 + oDD.padding[2];
18415             l = x1 - oDD.padding[3];
18416
18417             return new Roo.lib.Region( t, r, b, l );
18418         },
18419
18420         /**
18421          * Checks the cursor location to see if it over the target
18422          * @method isOverTarget
18423          * @param {Roo.lib.Point} pt The point to evaluate
18424          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18425          * @return {boolean} true if the mouse is over the target
18426          * @private
18427          * @static
18428          */
18429         isOverTarget: function(pt, oTarget, intersect) {
18430             // use cache if available
18431             var loc = this.locationCache[oTarget.id];
18432             if (!loc || !this.useCache) {
18433                 loc = this.getLocation(oTarget);
18434                 this.locationCache[oTarget.id] = loc;
18435
18436             }
18437
18438             if (!loc) {
18439                 return false;
18440             }
18441
18442             oTarget.cursorIsOver = loc.contains( pt );
18443
18444             // DragDrop is using this as a sanity check for the initial mousedown
18445             // in this case we are done.  In POINT mode, if the drag obj has no
18446             // contraints, we are also done. Otherwise we need to evaluate the
18447             // location of the target as related to the actual location of the
18448             // dragged element.
18449             var dc = this.dragCurrent;
18450             if (!dc || !dc.getTargetCoord ||
18451                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18452                 return oTarget.cursorIsOver;
18453             }
18454
18455             oTarget.overlap = null;
18456
18457             // Get the current location of the drag element, this is the
18458             // location of the mouse event less the delta that represents
18459             // where the original mousedown happened on the element.  We
18460             // need to consider constraints and ticks as well.
18461             var pos = dc.getTargetCoord(pt.x, pt.y);
18462
18463             var el = dc.getDragEl();
18464             var curRegion = new Roo.lib.Region( pos.y,
18465                                                    pos.x + el.offsetWidth,
18466                                                    pos.y + el.offsetHeight,
18467                                                    pos.x );
18468
18469             var overlap = curRegion.intersect(loc);
18470
18471             if (overlap) {
18472                 oTarget.overlap = overlap;
18473                 return (intersect) ? true : oTarget.cursorIsOver;
18474             } else {
18475                 return false;
18476             }
18477         },
18478
18479         /**
18480          * unload event handler
18481          * @method _onUnload
18482          * @private
18483          * @static
18484          */
18485         _onUnload: function(e, me) {
18486             Roo.dd.DragDropMgr.unregAll();
18487         },
18488
18489         /**
18490          * Cleans up the drag and drop events and objects.
18491          * @method unregAll
18492          * @private
18493          * @static
18494          */
18495         unregAll: function() {
18496
18497             if (this.dragCurrent) {
18498                 this.stopDrag();
18499                 this.dragCurrent = null;
18500             }
18501
18502             this._execOnAll("unreg", []);
18503
18504             for (i in this.elementCache) {
18505                 delete this.elementCache[i];
18506             }
18507
18508             this.elementCache = {};
18509             this.ids = {};
18510         },
18511
18512         /**
18513          * A cache of DOM elements
18514          * @property elementCache
18515          * @private
18516          * @static
18517          */
18518         elementCache: {},
18519
18520         /**
18521          * Get the wrapper for the DOM element specified
18522          * @method getElWrapper
18523          * @param {String} id the id of the element to get
18524          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18525          * @private
18526          * @deprecated This wrapper isn't that useful
18527          * @static
18528          */
18529         getElWrapper: function(id) {
18530             var oWrapper = this.elementCache[id];
18531             if (!oWrapper || !oWrapper.el) {
18532                 oWrapper = this.elementCache[id] =
18533                     new this.ElementWrapper(Roo.getDom(id));
18534             }
18535             return oWrapper;
18536         },
18537
18538         /**
18539          * Returns the actual DOM element
18540          * @method getElement
18541          * @param {String} id the id of the elment to get
18542          * @return {Object} The element
18543          * @deprecated use Roo.getDom instead
18544          * @static
18545          */
18546         getElement: function(id) {
18547             return Roo.getDom(id);
18548         },
18549
18550         /**
18551          * Returns the style property for the DOM element (i.e.,
18552          * document.getElById(id).style)
18553          * @method getCss
18554          * @param {String} id the id of the elment to get
18555          * @return {Object} The style property of the element
18556          * @deprecated use Roo.getDom instead
18557          * @static
18558          */
18559         getCss: function(id) {
18560             var el = Roo.getDom(id);
18561             return (el) ? el.style : null;
18562         },
18563
18564         /**
18565          * Inner class for cached elements
18566          * @class DragDropMgr.ElementWrapper
18567          * @for DragDropMgr
18568          * @private
18569          * @deprecated
18570          */
18571         ElementWrapper: function(el) {
18572                 /**
18573                  * The element
18574                  * @property el
18575                  */
18576                 this.el = el || null;
18577                 /**
18578                  * The element id
18579                  * @property id
18580                  */
18581                 this.id = this.el && el.id;
18582                 /**
18583                  * A reference to the style property
18584                  * @property css
18585                  */
18586                 this.css = this.el && el.style;
18587             },
18588
18589         /**
18590          * Returns the X position of an html element
18591          * @method getPosX
18592          * @param el the element for which to get the position
18593          * @return {int} the X coordinate
18594          * @for DragDropMgr
18595          * @deprecated use Roo.lib.Dom.getX instead
18596          * @static
18597          */
18598         getPosX: function(el) {
18599             return Roo.lib.Dom.getX(el);
18600         },
18601
18602         /**
18603          * Returns the Y position of an html element
18604          * @method getPosY
18605          * @param el the element for which to get the position
18606          * @return {int} the Y coordinate
18607          * @deprecated use Roo.lib.Dom.getY instead
18608          * @static
18609          */
18610         getPosY: function(el) {
18611             return Roo.lib.Dom.getY(el);
18612         },
18613
18614         /**
18615          * Swap two nodes.  In IE, we use the native method, for others we
18616          * emulate the IE behavior
18617          * @method swapNode
18618          * @param n1 the first node to swap
18619          * @param n2 the other node to swap
18620          * @static
18621          */
18622         swapNode: function(n1, n2) {
18623             if (n1.swapNode) {
18624                 n1.swapNode(n2);
18625             } else {
18626                 var p = n2.parentNode;
18627                 var s = n2.nextSibling;
18628
18629                 if (s == n1) {
18630                     p.insertBefore(n1, n2);
18631                 } else if (n2 == n1.nextSibling) {
18632                     p.insertBefore(n2, n1);
18633                 } else {
18634                     n1.parentNode.replaceChild(n2, n1);
18635                     p.insertBefore(n1, s);
18636                 }
18637             }
18638         },
18639
18640         /**
18641          * Returns the current scroll position
18642          * @method getScroll
18643          * @private
18644          * @static
18645          */
18646         getScroll: function () {
18647             var t, l, dde=document.documentElement, db=document.body;
18648             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18649                 t = dde.scrollTop;
18650                 l = dde.scrollLeft;
18651             } else if (db) {
18652                 t = db.scrollTop;
18653                 l = db.scrollLeft;
18654             } else {
18655
18656             }
18657             return { top: t, left: l };
18658         },
18659
18660         /**
18661          * Returns the specified element style property
18662          * @method getStyle
18663          * @param {HTMLElement} el          the element
18664          * @param {string}      styleProp   the style property
18665          * @return {string} The value of the style property
18666          * @deprecated use Roo.lib.Dom.getStyle
18667          * @static
18668          */
18669         getStyle: function(el, styleProp) {
18670             return Roo.fly(el).getStyle(styleProp);
18671         },
18672
18673         /**
18674          * Gets the scrollTop
18675          * @method getScrollTop
18676          * @return {int} the document's scrollTop
18677          * @static
18678          */
18679         getScrollTop: function () { return this.getScroll().top; },
18680
18681         /**
18682          * Gets the scrollLeft
18683          * @method getScrollLeft
18684          * @return {int} the document's scrollTop
18685          * @static
18686          */
18687         getScrollLeft: function () { return this.getScroll().left; },
18688
18689         /**
18690          * Sets the x/y position of an element to the location of the
18691          * target element.
18692          * @method moveToEl
18693          * @param {HTMLElement} moveEl      The element to move
18694          * @param {HTMLElement} targetEl    The position reference element
18695          * @static
18696          */
18697         moveToEl: function (moveEl, targetEl) {
18698             var aCoord = Roo.lib.Dom.getXY(targetEl);
18699             Roo.lib.Dom.setXY(moveEl, aCoord);
18700         },
18701
18702         /**
18703          * Numeric array sort function
18704          * @method numericSort
18705          * @static
18706          */
18707         numericSort: function(a, b) { return (a - b); },
18708
18709         /**
18710          * Internal counter
18711          * @property _timeoutCount
18712          * @private
18713          * @static
18714          */
18715         _timeoutCount: 0,
18716
18717         /**
18718          * Trying to make the load order less important.  Without this we get
18719          * an error if this file is loaded before the Event Utility.
18720          * @method _addListeners
18721          * @private
18722          * @static
18723          */
18724         _addListeners: function() {
18725             var DDM = Roo.dd.DDM;
18726             if ( Roo.lib.Event && document ) {
18727                 DDM._onLoad();
18728             } else {
18729                 if (DDM._timeoutCount > 2000) {
18730                 } else {
18731                     setTimeout(DDM._addListeners, 10);
18732                     if (document && document.body) {
18733                         DDM._timeoutCount += 1;
18734                     }
18735                 }
18736             }
18737         },
18738
18739         /**
18740          * Recursively searches the immediate parent and all child nodes for
18741          * the handle element in order to determine wheter or not it was
18742          * clicked.
18743          * @method handleWasClicked
18744          * @param node the html element to inspect
18745          * @static
18746          */
18747         handleWasClicked: function(node, id) {
18748             if (this.isHandle(id, node.id)) {
18749                 return true;
18750             } else {
18751                 // check to see if this is a text node child of the one we want
18752                 var p = node.parentNode;
18753
18754                 while (p) {
18755                     if (this.isHandle(id, p.id)) {
18756                         return true;
18757                     } else {
18758                         p = p.parentNode;
18759                     }
18760                 }
18761             }
18762
18763             return false;
18764         }
18765
18766     };
18767
18768 }();
18769
18770 // shorter alias, save a few bytes
18771 Roo.dd.DDM = Roo.dd.DragDropMgr;
18772 Roo.dd.DDM._addListeners();
18773
18774 }/*
18775  * Based on:
18776  * Ext JS Library 1.1.1
18777  * Copyright(c) 2006-2007, Ext JS, LLC.
18778  *
18779  * Originally Released Under LGPL - original licence link has changed is not relivant.
18780  *
18781  * Fork - LGPL
18782  * <script type="text/javascript">
18783  */
18784
18785 /**
18786  * @class Roo.dd.DD
18787  * A DragDrop implementation where the linked element follows the
18788  * mouse cursor during a drag.
18789  * @extends Roo.dd.DragDrop
18790  * @constructor
18791  * @param {String} id the id of the linked element
18792  * @param {String} sGroup the group of related DragDrop items
18793  * @param {object} config an object containing configurable attributes
18794  *                Valid properties for DD:
18795  *                    scroll
18796  */
18797 Roo.dd.DD = function(id, sGroup, config) {
18798     if (id) {
18799         this.init(id, sGroup, config);
18800     }
18801 };
18802
18803 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18804
18805     /**
18806      * When set to true, the utility automatically tries to scroll the browser
18807      * window wehn a drag and drop element is dragged near the viewport boundary.
18808      * Defaults to true.
18809      * @property scroll
18810      * @type boolean
18811      */
18812     scroll: true,
18813
18814     /**
18815      * Sets the pointer offset to the distance between the linked element's top
18816      * left corner and the location the element was clicked
18817      * @method autoOffset
18818      * @param {int} iPageX the X coordinate of the click
18819      * @param {int} iPageY the Y coordinate of the click
18820      */
18821     autoOffset: function(iPageX, iPageY) {
18822         var x = iPageX - this.startPageX;
18823         var y = iPageY - this.startPageY;
18824         this.setDelta(x, y);
18825     },
18826
18827     /**
18828      * Sets the pointer offset.  You can call this directly to force the
18829      * offset to be in a particular location (e.g., pass in 0,0 to set it
18830      * to the center of the object)
18831      * @method setDelta
18832      * @param {int} iDeltaX the distance from the left
18833      * @param {int} iDeltaY the distance from the top
18834      */
18835     setDelta: function(iDeltaX, iDeltaY) {
18836         this.deltaX = iDeltaX;
18837         this.deltaY = iDeltaY;
18838     },
18839
18840     /**
18841      * Sets the drag element to the location of the mousedown or click event,
18842      * maintaining the cursor location relative to the location on the element
18843      * that was clicked.  Override this if you want to place the element in a
18844      * location other than where the cursor is.
18845      * @method setDragElPos
18846      * @param {int} iPageX the X coordinate of the mousedown or drag event
18847      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18848      */
18849     setDragElPos: function(iPageX, iPageY) {
18850         // the first time we do this, we are going to check to make sure
18851         // the element has css positioning
18852
18853         var el = this.getDragEl();
18854         this.alignElWithMouse(el, iPageX, iPageY);
18855     },
18856
18857     /**
18858      * Sets the element to the location of the mousedown or click event,
18859      * maintaining the cursor location relative to the location on the element
18860      * that was clicked.  Override this if you want to place the element in a
18861      * location other than where the cursor is.
18862      * @method alignElWithMouse
18863      * @param {HTMLElement} el the element to move
18864      * @param {int} iPageX the X coordinate of the mousedown or drag event
18865      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18866      */
18867     alignElWithMouse: function(el, iPageX, iPageY) {
18868         var oCoord = this.getTargetCoord(iPageX, iPageY);
18869         var fly = el.dom ? el : Roo.fly(el);
18870         if (!this.deltaSetXY) {
18871             var aCoord = [oCoord.x, oCoord.y];
18872             fly.setXY(aCoord);
18873             var newLeft = fly.getLeft(true);
18874             var newTop  = fly.getTop(true);
18875             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18876         } else {
18877             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18878         }
18879
18880         this.cachePosition(oCoord.x, oCoord.y);
18881         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18882         return oCoord;
18883     },
18884
18885     /**
18886      * Saves the most recent position so that we can reset the constraints and
18887      * tick marks on-demand.  We need to know this so that we can calculate the
18888      * number of pixels the element is offset from its original position.
18889      * @method cachePosition
18890      * @param iPageX the current x position (optional, this just makes it so we
18891      * don't have to look it up again)
18892      * @param iPageY the current y position (optional, this just makes it so we
18893      * don't have to look it up again)
18894      */
18895     cachePosition: function(iPageX, iPageY) {
18896         if (iPageX) {
18897             this.lastPageX = iPageX;
18898             this.lastPageY = iPageY;
18899         } else {
18900             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18901             this.lastPageX = aCoord[0];
18902             this.lastPageY = aCoord[1];
18903         }
18904     },
18905
18906     /**
18907      * Auto-scroll the window if the dragged object has been moved beyond the
18908      * visible window boundary.
18909      * @method autoScroll
18910      * @param {int} x the drag element's x position
18911      * @param {int} y the drag element's y position
18912      * @param {int} h the height of the drag element
18913      * @param {int} w the width of the drag element
18914      * @private
18915      */
18916     autoScroll: function(x, y, h, w) {
18917
18918         if (this.scroll) {
18919             // The client height
18920             var clientH = Roo.lib.Dom.getViewWidth();
18921
18922             // The client width
18923             var clientW = Roo.lib.Dom.getViewHeight();
18924
18925             // The amt scrolled down
18926             var st = this.DDM.getScrollTop();
18927
18928             // The amt scrolled right
18929             var sl = this.DDM.getScrollLeft();
18930
18931             // Location of the bottom of the element
18932             var bot = h + y;
18933
18934             // Location of the right of the element
18935             var right = w + x;
18936
18937             // The distance from the cursor to the bottom of the visible area,
18938             // adjusted so that we don't scroll if the cursor is beyond the
18939             // element drag constraints
18940             var toBot = (clientH + st - y - this.deltaY);
18941
18942             // The distance from the cursor to the right of the visible area
18943             var toRight = (clientW + sl - x - this.deltaX);
18944
18945
18946             // How close to the edge the cursor must be before we scroll
18947             // var thresh = (document.all) ? 100 : 40;
18948             var thresh = 40;
18949
18950             // How many pixels to scroll per autoscroll op.  This helps to reduce
18951             // clunky scrolling. IE is more sensitive about this ... it needs this
18952             // value to be higher.
18953             var scrAmt = (document.all) ? 80 : 30;
18954
18955             // Scroll down if we are near the bottom of the visible page and the
18956             // obj extends below the crease
18957             if ( bot > clientH && toBot < thresh ) {
18958                 window.scrollTo(sl, st + scrAmt);
18959             }
18960
18961             // Scroll up if the window is scrolled down and the top of the object
18962             // goes above the top border
18963             if ( y < st && st > 0 && y - st < thresh ) {
18964                 window.scrollTo(sl, st - scrAmt);
18965             }
18966
18967             // Scroll right if the obj is beyond the right border and the cursor is
18968             // near the border.
18969             if ( right > clientW && toRight < thresh ) {
18970                 window.scrollTo(sl + scrAmt, st);
18971             }
18972
18973             // Scroll left if the window has been scrolled to the right and the obj
18974             // extends past the left border
18975             if ( x < sl && sl > 0 && x - sl < thresh ) {
18976                 window.scrollTo(sl - scrAmt, st);
18977             }
18978         }
18979     },
18980
18981     /**
18982      * Finds the location the element should be placed if we want to move
18983      * it to where the mouse location less the click offset would place us.
18984      * @method getTargetCoord
18985      * @param {int} iPageX the X coordinate of the click
18986      * @param {int} iPageY the Y coordinate of the click
18987      * @return an object that contains the coordinates (Object.x and Object.y)
18988      * @private
18989      */
18990     getTargetCoord: function(iPageX, iPageY) {
18991
18992
18993         var x = iPageX - this.deltaX;
18994         var y = iPageY - this.deltaY;
18995
18996         if (this.constrainX) {
18997             if (x < this.minX) { x = this.minX; }
18998             if (x > this.maxX) { x = this.maxX; }
18999         }
19000
19001         if (this.constrainY) {
19002             if (y < this.minY) { y = this.minY; }
19003             if (y > this.maxY) { y = this.maxY; }
19004         }
19005
19006         x = this.getTick(x, this.xTicks);
19007         y = this.getTick(y, this.yTicks);
19008
19009
19010         return {x:x, y:y};
19011     },
19012
19013     /*
19014      * Sets up config options specific to this class. Overrides
19015      * Roo.dd.DragDrop, but all versions of this method through the
19016      * inheritance chain are called
19017      */
19018     applyConfig: function() {
19019         Roo.dd.DD.superclass.applyConfig.call(this);
19020         this.scroll = (this.config.scroll !== false);
19021     },
19022
19023     /*
19024      * Event that fires prior to the onMouseDown event.  Overrides
19025      * Roo.dd.DragDrop.
19026      */
19027     b4MouseDown: function(e) {
19028         // this.resetConstraints();
19029         this.autoOffset(e.getPageX(),
19030                             e.getPageY());
19031     },
19032
19033     /*
19034      * Event that fires prior to the onDrag event.  Overrides
19035      * Roo.dd.DragDrop.
19036      */
19037     b4Drag: function(e) {
19038         this.setDragElPos(e.getPageX(),
19039                             e.getPageY());
19040     },
19041
19042     toString: function() {
19043         return ("DD " + this.id);
19044     }
19045
19046     //////////////////////////////////////////////////////////////////////////
19047     // Debugging ygDragDrop events that can be overridden
19048     //////////////////////////////////////////////////////////////////////////
19049     /*
19050     startDrag: function(x, y) {
19051     },
19052
19053     onDrag: function(e) {
19054     },
19055
19056     onDragEnter: function(e, id) {
19057     },
19058
19059     onDragOver: function(e, id) {
19060     },
19061
19062     onDragOut: function(e, id) {
19063     },
19064
19065     onDragDrop: function(e, id) {
19066     },
19067
19068     endDrag: function(e) {
19069     }
19070
19071     */
19072
19073 });/*
19074  * Based on:
19075  * Ext JS Library 1.1.1
19076  * Copyright(c) 2006-2007, Ext JS, LLC.
19077  *
19078  * Originally Released Under LGPL - original licence link has changed is not relivant.
19079  *
19080  * Fork - LGPL
19081  * <script type="text/javascript">
19082  */
19083
19084 /**
19085  * @class Roo.dd.DDProxy
19086  * A DragDrop implementation that inserts an empty, bordered div into
19087  * the document that follows the cursor during drag operations.  At the time of
19088  * the click, the frame div is resized to the dimensions of the linked html
19089  * element, and moved to the exact location of the linked element.
19090  *
19091  * References to the "frame" element refer to the single proxy element that
19092  * was created to be dragged in place of all DDProxy elements on the
19093  * page.
19094  *
19095  * @extends Roo.dd.DD
19096  * @constructor
19097  * @param {String} id the id of the linked html element
19098  * @param {String} sGroup the group of related DragDrop objects
19099  * @param {object} config an object containing configurable attributes
19100  *                Valid properties for DDProxy in addition to those in DragDrop:
19101  *                   resizeFrame, centerFrame, dragElId
19102  */
19103 Roo.dd.DDProxy = function(id, sGroup, config) {
19104     if (id) {
19105         this.init(id, sGroup, config);
19106         this.initFrame();
19107     }
19108 };
19109
19110 /**
19111  * The default drag frame div id
19112  * @property Roo.dd.DDProxy.dragElId
19113  * @type String
19114  * @static
19115  */
19116 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19117
19118 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19119
19120     /**
19121      * By default we resize the drag frame to be the same size as the element
19122      * we want to drag (this is to get the frame effect).  We can turn it off
19123      * if we want a different behavior.
19124      * @property resizeFrame
19125      * @type boolean
19126      */
19127     resizeFrame: true,
19128
19129     /**
19130      * By default the frame is positioned exactly where the drag element is, so
19131      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19132      * you do not have constraints on the obj is to have the drag frame centered
19133      * around the cursor.  Set centerFrame to true for this effect.
19134      * @property centerFrame
19135      * @type boolean
19136      */
19137     centerFrame: false,
19138
19139     /**
19140      * Creates the proxy element if it does not yet exist
19141      * @method createFrame
19142      */
19143     createFrame: function() {
19144         var self = this;
19145         var body = document.body;
19146
19147         if (!body || !body.firstChild) {
19148             setTimeout( function() { self.createFrame(); }, 50 );
19149             return;
19150         }
19151
19152         var div = this.getDragEl();
19153
19154         if (!div) {
19155             div    = document.createElement("div");
19156             div.id = this.dragElId;
19157             var s  = div.style;
19158
19159             s.position   = "absolute";
19160             s.visibility = "hidden";
19161             s.cursor     = "move";
19162             s.border     = "2px solid #aaa";
19163             s.zIndex     = 999;
19164
19165             // appendChild can blow up IE if invoked prior to the window load event
19166             // while rendering a table.  It is possible there are other scenarios
19167             // that would cause this to happen as well.
19168             body.insertBefore(div, body.firstChild);
19169         }
19170     },
19171
19172     /**
19173      * Initialization for the drag frame element.  Must be called in the
19174      * constructor of all subclasses
19175      * @method initFrame
19176      */
19177     initFrame: function() {
19178         this.createFrame();
19179     },
19180
19181     applyConfig: function() {
19182         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19183
19184         this.resizeFrame = (this.config.resizeFrame !== false);
19185         this.centerFrame = (this.config.centerFrame);
19186         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19187     },
19188
19189     /**
19190      * Resizes the drag frame to the dimensions of the clicked object, positions
19191      * it over the object, and finally displays it
19192      * @method showFrame
19193      * @param {int} iPageX X click position
19194      * @param {int} iPageY Y click position
19195      * @private
19196      */
19197     showFrame: function(iPageX, iPageY) {
19198         var el = this.getEl();
19199         var dragEl = this.getDragEl();
19200         var s = dragEl.style;
19201
19202         this._resizeProxy();
19203
19204         if (this.centerFrame) {
19205             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19206                            Math.round(parseInt(s.height, 10)/2) );
19207         }
19208
19209         this.setDragElPos(iPageX, iPageY);
19210
19211         Roo.fly(dragEl).show();
19212     },
19213
19214     /**
19215      * The proxy is automatically resized to the dimensions of the linked
19216      * element when a drag is initiated, unless resizeFrame is set to false
19217      * @method _resizeProxy
19218      * @private
19219      */
19220     _resizeProxy: function() {
19221         if (this.resizeFrame) {
19222             var el = this.getEl();
19223             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19224         }
19225     },
19226
19227     // overrides Roo.dd.DragDrop
19228     b4MouseDown: function(e) {
19229         var x = e.getPageX();
19230         var y = e.getPageY();
19231         this.autoOffset(x, y);
19232         this.setDragElPos(x, y);
19233     },
19234
19235     // overrides Roo.dd.DragDrop
19236     b4StartDrag: function(x, y) {
19237         // show the drag frame
19238         this.showFrame(x, y);
19239     },
19240
19241     // overrides Roo.dd.DragDrop
19242     b4EndDrag: function(e) {
19243         Roo.fly(this.getDragEl()).hide();
19244     },
19245
19246     // overrides Roo.dd.DragDrop
19247     // By default we try to move the element to the last location of the frame.
19248     // This is so that the default behavior mirrors that of Roo.dd.DD.
19249     endDrag: function(e) {
19250
19251         var lel = this.getEl();
19252         var del = this.getDragEl();
19253
19254         // Show the drag frame briefly so we can get its position
19255         del.style.visibility = "";
19256
19257         this.beforeMove();
19258         // Hide the linked element before the move to get around a Safari
19259         // rendering bug.
19260         lel.style.visibility = "hidden";
19261         Roo.dd.DDM.moveToEl(lel, del);
19262         del.style.visibility = "hidden";
19263         lel.style.visibility = "";
19264
19265         this.afterDrag();
19266     },
19267
19268     beforeMove : function(){
19269
19270     },
19271
19272     afterDrag : function(){
19273
19274     },
19275
19276     toString: function() {
19277         return ("DDProxy " + this.id);
19278     }
19279
19280 });
19281 /*
19282  * Based on:
19283  * Ext JS Library 1.1.1
19284  * Copyright(c) 2006-2007, Ext JS, LLC.
19285  *
19286  * Originally Released Under LGPL - original licence link has changed is not relivant.
19287  *
19288  * Fork - LGPL
19289  * <script type="text/javascript">
19290  */
19291
19292  /**
19293  * @class Roo.dd.DDTarget
19294  * A DragDrop implementation that does not move, but can be a drop
19295  * target.  You would get the same result by simply omitting implementation
19296  * for the event callbacks, but this way we reduce the processing cost of the
19297  * event listener and the callbacks.
19298  * @extends Roo.dd.DragDrop
19299  * @constructor
19300  * @param {String} id the id of the element that is a drop target
19301  * @param {String} sGroup the group of related DragDrop objects
19302  * @param {object} config an object containing configurable attributes
19303  *                 Valid properties for DDTarget in addition to those in
19304  *                 DragDrop:
19305  *                    none
19306  */
19307 Roo.dd.DDTarget = function(id, sGroup, config) {
19308     if (id) {
19309         this.initTarget(id, sGroup, config);
19310     }
19311     if (config.listeners || config.events) { 
19312        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19313             listeners : config.listeners || {}, 
19314             events : config.events || {} 
19315         });    
19316     }
19317 };
19318
19319 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19320 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19321     toString: function() {
19322         return ("DDTarget " + this.id);
19323     }
19324 });
19325 /*
19326  * Based on:
19327  * Ext JS Library 1.1.1
19328  * Copyright(c) 2006-2007, Ext JS, LLC.
19329  *
19330  * Originally Released Under LGPL - original licence link has changed is not relivant.
19331  *
19332  * Fork - LGPL
19333  * <script type="text/javascript">
19334  */
19335  
19336
19337 /**
19338  * @class Roo.dd.ScrollManager
19339  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19340  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19341  * @singleton
19342  */
19343 Roo.dd.ScrollManager = function(){
19344     var ddm = Roo.dd.DragDropMgr;
19345     var els = {};
19346     var dragEl = null;
19347     var proc = {};
19348     
19349     
19350     
19351     var onStop = function(e){
19352         dragEl = null;
19353         clearProc();
19354     };
19355     
19356     var triggerRefresh = function(){
19357         if(ddm.dragCurrent){
19358              ddm.refreshCache(ddm.dragCurrent.groups);
19359         }
19360     };
19361     
19362     var doScroll = function(){
19363         if(ddm.dragCurrent){
19364             var dds = Roo.dd.ScrollManager;
19365             if(!dds.animate){
19366                 if(proc.el.scroll(proc.dir, dds.increment)){
19367                     triggerRefresh();
19368                 }
19369             }else{
19370                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19371             }
19372         }
19373     };
19374     
19375     var clearProc = function(){
19376         if(proc.id){
19377             clearInterval(proc.id);
19378         }
19379         proc.id = 0;
19380         proc.el = null;
19381         proc.dir = "";
19382     };
19383     
19384     var startProc = function(el, dir){
19385          Roo.log('scroll startproc');
19386         clearProc();
19387         proc.el = el;
19388         proc.dir = dir;
19389         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19390     };
19391     
19392     var onFire = function(e, isDrop){
19393        
19394         if(isDrop || !ddm.dragCurrent){ return; }
19395         var dds = Roo.dd.ScrollManager;
19396         if(!dragEl || dragEl != ddm.dragCurrent){
19397             dragEl = ddm.dragCurrent;
19398             // refresh regions on drag start
19399             dds.refreshCache();
19400         }
19401         
19402         var xy = Roo.lib.Event.getXY(e);
19403         var pt = new Roo.lib.Point(xy[0], xy[1]);
19404         for(var id in els){
19405             var el = els[id], r = el._region;
19406             if(r && r.contains(pt) && el.isScrollable()){
19407                 if(r.bottom - pt.y <= dds.thresh){
19408                     if(proc.el != el){
19409                         startProc(el, "down");
19410                     }
19411                     return;
19412                 }else if(r.right - pt.x <= dds.thresh){
19413                     if(proc.el != el){
19414                         startProc(el, "left");
19415                     }
19416                     return;
19417                 }else if(pt.y - r.top <= dds.thresh){
19418                     if(proc.el != el){
19419                         startProc(el, "up");
19420                     }
19421                     return;
19422                 }else if(pt.x - r.left <= dds.thresh){
19423                     if(proc.el != el){
19424                         startProc(el, "right");
19425                     }
19426                     return;
19427                 }
19428             }
19429         }
19430         clearProc();
19431     };
19432     
19433     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19434     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19435     
19436     return {
19437         /**
19438          * Registers new overflow element(s) to auto scroll
19439          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19440          */
19441         register : function(el){
19442             if(el instanceof Array){
19443                 for(var i = 0, len = el.length; i < len; i++) {
19444                         this.register(el[i]);
19445                 }
19446             }else{
19447                 el = Roo.get(el);
19448                 els[el.id] = el;
19449             }
19450             Roo.dd.ScrollManager.els = els;
19451         },
19452         
19453         /**
19454          * Unregisters overflow element(s) so they are no longer scrolled
19455          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19456          */
19457         unregister : function(el){
19458             if(el instanceof Array){
19459                 for(var i = 0, len = el.length; i < len; i++) {
19460                         this.unregister(el[i]);
19461                 }
19462             }else{
19463                 el = Roo.get(el);
19464                 delete els[el.id];
19465             }
19466         },
19467         
19468         /**
19469          * The number of pixels from the edge of a container the pointer needs to be to 
19470          * trigger scrolling (defaults to 25)
19471          * @type Number
19472          */
19473         thresh : 25,
19474         
19475         /**
19476          * The number of pixels to scroll in each scroll increment (defaults to 50)
19477          * @type Number
19478          */
19479         increment : 100,
19480         
19481         /**
19482          * The frequency of scrolls in milliseconds (defaults to 500)
19483          * @type Number
19484          */
19485         frequency : 500,
19486         
19487         /**
19488          * True to animate the scroll (defaults to true)
19489          * @type Boolean
19490          */
19491         animate: true,
19492         
19493         /**
19494          * The animation duration in seconds - 
19495          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19496          * @type Number
19497          */
19498         animDuration: .4,
19499         
19500         /**
19501          * Manually trigger a cache refresh.
19502          */
19503         refreshCache : function(){
19504             for(var id in els){
19505                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19506                     els[id]._region = els[id].getRegion();
19507                 }
19508             }
19509         }
19510     };
19511 }();/*
19512  * Based on:
19513  * Ext JS Library 1.1.1
19514  * Copyright(c) 2006-2007, Ext JS, LLC.
19515  *
19516  * Originally Released Under LGPL - original licence link has changed is not relivant.
19517  *
19518  * Fork - LGPL
19519  * <script type="text/javascript">
19520  */
19521  
19522
19523 /**
19524  * @class Roo.dd.Registry
19525  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19526  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19527  * @singleton
19528  */
19529 Roo.dd.Registry = function(){
19530     var elements = {}; 
19531     var handles = {}; 
19532     var autoIdSeed = 0;
19533
19534     var getId = function(el, autogen){
19535         if(typeof el == "string"){
19536             return el;
19537         }
19538         var id = el.id;
19539         if(!id && autogen !== false){
19540             id = "roodd-" + (++autoIdSeed);
19541             el.id = id;
19542         }
19543         return id;
19544     };
19545     
19546     return {
19547     /**
19548      * Register a drag drop element
19549      * @param {String|HTMLElement} element The id or DOM node to register
19550      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19551      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19552      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19553      * populated in the data object (if applicable):
19554      * <pre>
19555 Value      Description<br />
19556 ---------  ------------------------------------------<br />
19557 handles    Array of DOM nodes that trigger dragging<br />
19558            for the element being registered<br />
19559 isHandle   True if the element passed in triggers<br />
19560            dragging itself, else false
19561 </pre>
19562      */
19563         register : function(el, data){
19564             data = data || {};
19565             if(typeof el == "string"){
19566                 el = document.getElementById(el);
19567             }
19568             data.ddel = el;
19569             elements[getId(el)] = data;
19570             if(data.isHandle !== false){
19571                 handles[data.ddel.id] = data;
19572             }
19573             if(data.handles){
19574                 var hs = data.handles;
19575                 for(var i = 0, len = hs.length; i < len; i++){
19576                         handles[getId(hs[i])] = data;
19577                 }
19578             }
19579         },
19580
19581     /**
19582      * Unregister a drag drop element
19583      * @param {String|HTMLElement}  element The id or DOM node to unregister
19584      */
19585         unregister : function(el){
19586             var id = getId(el, false);
19587             var data = elements[id];
19588             if(data){
19589                 delete elements[id];
19590                 if(data.handles){
19591                     var hs = data.handles;
19592                     for(var i = 0, len = hs.length; i < len; i++){
19593                         delete handles[getId(hs[i], false)];
19594                     }
19595                 }
19596             }
19597         },
19598
19599     /**
19600      * Returns the handle registered for a DOM Node by id
19601      * @param {String|HTMLElement} id The DOM node or id to look up
19602      * @return {Object} handle The custom handle data
19603      */
19604         getHandle : function(id){
19605             if(typeof id != "string"){ // must be element?
19606                 id = id.id;
19607             }
19608             return handles[id];
19609         },
19610
19611     /**
19612      * Returns the handle that is registered for the DOM node that is the target of the event
19613      * @param {Event} e The event
19614      * @return {Object} handle The custom handle data
19615      */
19616         getHandleFromEvent : function(e){
19617             var t = Roo.lib.Event.getTarget(e);
19618             return t ? handles[t.id] : null;
19619         },
19620
19621     /**
19622      * Returns a custom data object that is registered for a DOM node by id
19623      * @param {String|HTMLElement} id The DOM node or id to look up
19624      * @return {Object} data The custom data
19625      */
19626         getTarget : function(id){
19627             if(typeof id != "string"){ // must be element?
19628                 id = id.id;
19629             }
19630             return elements[id];
19631         },
19632
19633     /**
19634      * Returns a custom data object that is registered for the DOM node that is the target of the event
19635      * @param {Event} e The event
19636      * @return {Object} data The custom data
19637      */
19638         getTargetFromEvent : function(e){
19639             var t = Roo.lib.Event.getTarget(e);
19640             return t ? elements[t.id] || handles[t.id] : null;
19641         }
19642     };
19643 }();/*
19644  * Based on:
19645  * Ext JS Library 1.1.1
19646  * Copyright(c) 2006-2007, Ext JS, LLC.
19647  *
19648  * Originally Released Under LGPL - original licence link has changed is not relivant.
19649  *
19650  * Fork - LGPL
19651  * <script type="text/javascript">
19652  */
19653  
19654
19655 /**
19656  * @class Roo.dd.StatusProxy
19657  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19658  * default drag proxy used by all Roo.dd components.
19659  * @constructor
19660  * @param {Object} config
19661  */
19662 Roo.dd.StatusProxy = function(config){
19663     Roo.apply(this, config);
19664     this.id = this.id || Roo.id();
19665     this.el = new Roo.Layer({
19666         dh: {
19667             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19668                 {tag: "div", cls: "x-dd-drop-icon"},
19669                 {tag: "div", cls: "x-dd-drag-ghost"}
19670             ]
19671         }, 
19672         shadow: !config || config.shadow !== false
19673     });
19674     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19675     this.dropStatus = this.dropNotAllowed;
19676 };
19677
19678 Roo.dd.StatusProxy.prototype = {
19679     /**
19680      * @cfg {String} dropAllowed
19681      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19682      */
19683     dropAllowed : "x-dd-drop-ok",
19684     /**
19685      * @cfg {String} dropNotAllowed
19686      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19687      */
19688     dropNotAllowed : "x-dd-drop-nodrop",
19689
19690     /**
19691      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19692      * over the current target element.
19693      * @param {String} cssClass The css class for the new drop status indicator image
19694      */
19695     setStatus : function(cssClass){
19696         cssClass = cssClass || this.dropNotAllowed;
19697         if(this.dropStatus != cssClass){
19698             this.el.replaceClass(this.dropStatus, cssClass);
19699             this.dropStatus = cssClass;
19700         }
19701     },
19702
19703     /**
19704      * Resets the status indicator to the default dropNotAllowed value
19705      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19706      */
19707     reset : function(clearGhost){
19708         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19709         this.dropStatus = this.dropNotAllowed;
19710         if(clearGhost){
19711             this.ghost.update("");
19712         }
19713     },
19714
19715     /**
19716      * Updates the contents of the ghost element
19717      * @param {String} html The html that will replace the current innerHTML of the ghost element
19718      */
19719     update : function(html){
19720         if(typeof html == "string"){
19721             this.ghost.update(html);
19722         }else{
19723             this.ghost.update("");
19724             html.style.margin = "0";
19725             this.ghost.dom.appendChild(html);
19726         }
19727         // ensure float = none set?? cant remember why though.
19728         var el = this.ghost.dom.firstChild;
19729                 if(el){
19730                         Roo.fly(el).setStyle('float', 'none');
19731                 }
19732     },
19733     
19734     /**
19735      * Returns the underlying proxy {@link Roo.Layer}
19736      * @return {Roo.Layer} el
19737     */
19738     getEl : function(){
19739         return this.el;
19740     },
19741
19742     /**
19743      * Returns the ghost element
19744      * @return {Roo.Element} el
19745      */
19746     getGhost : function(){
19747         return this.ghost;
19748     },
19749
19750     /**
19751      * Hides the proxy
19752      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19753      */
19754     hide : function(clear){
19755         this.el.hide();
19756         if(clear){
19757             this.reset(true);
19758         }
19759     },
19760
19761     /**
19762      * Stops the repair animation if it's currently running
19763      */
19764     stop : function(){
19765         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19766             this.anim.stop();
19767         }
19768     },
19769
19770     /**
19771      * Displays this proxy
19772      */
19773     show : function(){
19774         this.el.show();
19775     },
19776
19777     /**
19778      * Force the Layer to sync its shadow and shim positions to the element
19779      */
19780     sync : function(){
19781         this.el.sync();
19782     },
19783
19784     /**
19785      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19786      * invalid drop operation by the item being dragged.
19787      * @param {Array} xy The XY position of the element ([x, y])
19788      * @param {Function} callback The function to call after the repair is complete
19789      * @param {Object} scope The scope in which to execute the callback
19790      */
19791     repair : function(xy, callback, scope){
19792         this.callback = callback;
19793         this.scope = scope;
19794         if(xy && this.animRepair !== false){
19795             this.el.addClass("x-dd-drag-repair");
19796             this.el.hideUnders(true);
19797             this.anim = this.el.shift({
19798                 duration: this.repairDuration || .5,
19799                 easing: 'easeOut',
19800                 xy: xy,
19801                 stopFx: true,
19802                 callback: this.afterRepair,
19803                 scope: this
19804             });
19805         }else{
19806             this.afterRepair();
19807         }
19808     },
19809
19810     // private
19811     afterRepair : function(){
19812         this.hide(true);
19813         if(typeof this.callback == "function"){
19814             this.callback.call(this.scope || this);
19815         }
19816         this.callback = null;
19817         this.scope = null;
19818     }
19819 };/*
19820  * Based on:
19821  * Ext JS Library 1.1.1
19822  * Copyright(c) 2006-2007, Ext JS, LLC.
19823  *
19824  * Originally Released Under LGPL - original licence link has changed is not relivant.
19825  *
19826  * Fork - LGPL
19827  * <script type="text/javascript">
19828  */
19829
19830 /**
19831  * @class Roo.dd.DragSource
19832  * @extends Roo.dd.DDProxy
19833  * A simple class that provides the basic implementation needed to make any element draggable.
19834  * @constructor
19835  * @param {String/HTMLElement/Element} el The container element
19836  * @param {Object} config
19837  */
19838 Roo.dd.DragSource = function(el, config){
19839     this.el = Roo.get(el);
19840     this.dragData = {};
19841     
19842     Roo.apply(this, config);
19843     
19844     if(!this.proxy){
19845         this.proxy = new Roo.dd.StatusProxy();
19846     }
19847
19848     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19849           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19850     
19851     this.dragging = false;
19852 };
19853
19854 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19855     /**
19856      * @cfg {String} dropAllowed
19857      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19858      */
19859     dropAllowed : "x-dd-drop-ok",
19860     /**
19861      * @cfg {String} dropNotAllowed
19862      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19863      */
19864     dropNotAllowed : "x-dd-drop-nodrop",
19865
19866     /**
19867      * Returns the data object associated with this drag source
19868      * @return {Object} data An object containing arbitrary data
19869      */
19870     getDragData : function(e){
19871         return this.dragData;
19872     },
19873
19874     // private
19875     onDragEnter : function(e, id){
19876         var target = Roo.dd.DragDropMgr.getDDById(id);
19877         this.cachedTarget = target;
19878         if(this.beforeDragEnter(target, e, id) !== false){
19879             if(target.isNotifyTarget){
19880                 var status = target.notifyEnter(this, e, this.dragData);
19881                 this.proxy.setStatus(status);
19882             }else{
19883                 this.proxy.setStatus(this.dropAllowed);
19884             }
19885             
19886             if(this.afterDragEnter){
19887                 /**
19888                  * An empty function by default, but provided so that you can perform a custom action
19889                  * when the dragged item enters the drop target by providing an implementation.
19890                  * @param {Roo.dd.DragDrop} target The drop target
19891                  * @param {Event} e The event object
19892                  * @param {String} id The id of the dragged element
19893                  * @method afterDragEnter
19894                  */
19895                 this.afterDragEnter(target, e, id);
19896             }
19897         }
19898     },
19899
19900     /**
19901      * An empty function by default, but provided so that you can perform a custom action
19902      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19903      * @param {Roo.dd.DragDrop} target The drop target
19904      * @param {Event} e The event object
19905      * @param {String} id The id of the dragged element
19906      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19907      */
19908     beforeDragEnter : function(target, e, id){
19909         return true;
19910     },
19911
19912     // private
19913     alignElWithMouse: function() {
19914         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19915         this.proxy.sync();
19916     },
19917
19918     // private
19919     onDragOver : function(e, id){
19920         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19921         if(this.beforeDragOver(target, e, id) !== false){
19922             if(target.isNotifyTarget){
19923                 var status = target.notifyOver(this, e, this.dragData);
19924                 this.proxy.setStatus(status);
19925             }
19926
19927             if(this.afterDragOver){
19928                 /**
19929                  * An empty function by default, but provided so that you can perform a custom action
19930                  * while the dragged item is over the drop target by providing an implementation.
19931                  * @param {Roo.dd.DragDrop} target The drop target
19932                  * @param {Event} e The event object
19933                  * @param {String} id The id of the dragged element
19934                  * @method afterDragOver
19935                  */
19936                 this.afterDragOver(target, e, id);
19937             }
19938         }
19939     },
19940
19941     /**
19942      * An empty function by default, but provided so that you can perform a custom action
19943      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19944      * @param {Roo.dd.DragDrop} target The drop target
19945      * @param {Event} e The event object
19946      * @param {String} id The id of the dragged element
19947      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19948      */
19949     beforeDragOver : function(target, e, id){
19950         return true;
19951     },
19952
19953     // private
19954     onDragOut : function(e, id){
19955         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19956         if(this.beforeDragOut(target, e, id) !== false){
19957             if(target.isNotifyTarget){
19958                 target.notifyOut(this, e, this.dragData);
19959             }
19960             this.proxy.reset();
19961             if(this.afterDragOut){
19962                 /**
19963                  * An empty function by default, but provided so that you can perform a custom action
19964                  * after the dragged item is dragged out of the target without dropping.
19965                  * @param {Roo.dd.DragDrop} target The drop target
19966                  * @param {Event} e The event object
19967                  * @param {String} id The id of the dragged element
19968                  * @method afterDragOut
19969                  */
19970                 this.afterDragOut(target, e, id);
19971             }
19972         }
19973         this.cachedTarget = null;
19974     },
19975
19976     /**
19977      * An empty function by default, but provided so that you can perform a custom action before the dragged
19978      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
19979      * @param {Roo.dd.DragDrop} target The drop target
19980      * @param {Event} e The event object
19981      * @param {String} id The id of the dragged element
19982      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19983      */
19984     beforeDragOut : function(target, e, id){
19985         return true;
19986     },
19987     
19988     // private
19989     onDragDrop : function(e, id){
19990         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19991         if(this.beforeDragDrop(target, e, id) !== false){
19992             if(target.isNotifyTarget){
19993                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
19994                     this.onValidDrop(target, e, id);
19995                 }else{
19996                     this.onInvalidDrop(target, e, id);
19997                 }
19998             }else{
19999                 this.onValidDrop(target, e, id);
20000             }
20001             
20002             if(this.afterDragDrop){
20003                 /**
20004                  * An empty function by default, but provided so that you can perform a custom action
20005                  * after a valid drag drop has occurred by providing an implementation.
20006                  * @param {Roo.dd.DragDrop} target The drop target
20007                  * @param {Event} e The event object
20008                  * @param {String} id The id of the dropped element
20009                  * @method afterDragDrop
20010                  */
20011                 this.afterDragDrop(target, e, id);
20012             }
20013         }
20014         delete this.cachedTarget;
20015     },
20016
20017     /**
20018      * An empty function by default, but provided so that you can perform a custom action before the dragged
20019      * item is dropped onto the target and optionally cancel the onDragDrop.
20020      * @param {Roo.dd.DragDrop} target The drop target
20021      * @param {Event} e The event object
20022      * @param {String} id The id of the dragged element
20023      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20024      */
20025     beforeDragDrop : function(target, e, id){
20026         return true;
20027     },
20028
20029     // private
20030     onValidDrop : function(target, e, id){
20031         this.hideProxy();
20032         if(this.afterValidDrop){
20033             /**
20034              * An empty function by default, but provided so that you can perform a custom action
20035              * after a valid drop has occurred by providing an implementation.
20036              * @param {Object} target The target DD 
20037              * @param {Event} e The event object
20038              * @param {String} id The id of the dropped element
20039              * @method afterInvalidDrop
20040              */
20041             this.afterValidDrop(target, e, id);
20042         }
20043     },
20044
20045     // private
20046     getRepairXY : function(e, data){
20047         return this.el.getXY();  
20048     },
20049
20050     // private
20051     onInvalidDrop : function(target, e, id){
20052         this.beforeInvalidDrop(target, e, id);
20053         if(this.cachedTarget){
20054             if(this.cachedTarget.isNotifyTarget){
20055                 this.cachedTarget.notifyOut(this, e, this.dragData);
20056             }
20057             this.cacheTarget = null;
20058         }
20059         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20060
20061         if(this.afterInvalidDrop){
20062             /**
20063              * An empty function by default, but provided so that you can perform a custom action
20064              * after an invalid drop has occurred by providing an implementation.
20065              * @param {Event} e The event object
20066              * @param {String} id The id of the dropped element
20067              * @method afterInvalidDrop
20068              */
20069             this.afterInvalidDrop(e, id);
20070         }
20071     },
20072
20073     // private
20074     afterRepair : function(){
20075         if(Roo.enableFx){
20076             this.el.highlight(this.hlColor || "c3daf9");
20077         }
20078         this.dragging = false;
20079     },
20080
20081     /**
20082      * An empty function by default, but provided so that you can perform a custom action after an invalid
20083      * drop has occurred.
20084      * @param {Roo.dd.DragDrop} target The drop target
20085      * @param {Event} e The event object
20086      * @param {String} id The id of the dragged element
20087      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20088      */
20089     beforeInvalidDrop : function(target, e, id){
20090         return true;
20091     },
20092
20093     // private
20094     handleMouseDown : function(e){
20095         if(this.dragging) {
20096             return;
20097         }
20098         var data = this.getDragData(e);
20099         if(data && this.onBeforeDrag(data, e) !== false){
20100             this.dragData = data;
20101             this.proxy.stop();
20102             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20103         } 
20104     },
20105
20106     /**
20107      * An empty function by default, but provided so that you can perform a custom action before the initial
20108      * drag event begins and optionally cancel it.
20109      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20110      * @param {Event} e The event object
20111      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20112      */
20113     onBeforeDrag : function(data, e){
20114         return true;
20115     },
20116
20117     /**
20118      * An empty function by default, but provided so that you can perform a custom action once the initial
20119      * drag event has begun.  The drag cannot be canceled from this function.
20120      * @param {Number} x The x position of the click on the dragged object
20121      * @param {Number} y The y position of the click on the dragged object
20122      */
20123     onStartDrag : Roo.emptyFn,
20124
20125     // private - YUI override
20126     startDrag : function(x, y){
20127         this.proxy.reset();
20128         this.dragging = true;
20129         this.proxy.update("");
20130         this.onInitDrag(x, y);
20131         this.proxy.show();
20132     },
20133
20134     // private
20135     onInitDrag : function(x, y){
20136         var clone = this.el.dom.cloneNode(true);
20137         clone.id = Roo.id(); // prevent duplicate ids
20138         this.proxy.update(clone);
20139         this.onStartDrag(x, y);
20140         return true;
20141     },
20142
20143     /**
20144      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20145      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20146      */
20147     getProxy : function(){
20148         return this.proxy;  
20149     },
20150
20151     /**
20152      * Hides the drag source's {@link Roo.dd.StatusProxy}
20153      */
20154     hideProxy : function(){
20155         this.proxy.hide();  
20156         this.proxy.reset(true);
20157         this.dragging = false;
20158     },
20159
20160     // private
20161     triggerCacheRefresh : function(){
20162         Roo.dd.DDM.refreshCache(this.groups);
20163     },
20164
20165     // private - override to prevent hiding
20166     b4EndDrag: function(e) {
20167     },
20168
20169     // private - override to prevent moving
20170     endDrag : function(e){
20171         this.onEndDrag(this.dragData, e);
20172     },
20173
20174     // private
20175     onEndDrag : function(data, e){
20176     },
20177     
20178     // private - pin to cursor
20179     autoOffset : function(x, y) {
20180         this.setDelta(-12, -20);
20181     }    
20182 });/*
20183  * Based on:
20184  * Ext JS Library 1.1.1
20185  * Copyright(c) 2006-2007, Ext JS, LLC.
20186  *
20187  * Originally Released Under LGPL - original licence link has changed is not relivant.
20188  *
20189  * Fork - LGPL
20190  * <script type="text/javascript">
20191  */
20192
20193
20194 /**
20195  * @class Roo.dd.DropTarget
20196  * @extends Roo.dd.DDTarget
20197  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20198  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20199  * @constructor
20200  * @param {String/HTMLElement/Element} el The container element
20201  * @param {Object} config
20202  */
20203 Roo.dd.DropTarget = function(el, config){
20204     this.el = Roo.get(el);
20205     
20206     var listeners = false; ;
20207     if (config && config.listeners) {
20208         listeners= config.listeners;
20209         delete config.listeners;
20210     }
20211     Roo.apply(this, config);
20212     
20213     if(this.containerScroll){
20214         Roo.dd.ScrollManager.register(this.el);
20215     }
20216     this.addEvents( {
20217          /**
20218          * @scope Roo.dd.DropTarget
20219          */
20220          
20221          /**
20222          * @event enter
20223          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20224          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20225          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20226          * 
20227          * IMPORTANT : it should set this.overClass and this.dropAllowed
20228          * 
20229          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20230          * @param {Event} e The event
20231          * @param {Object} data An object containing arbitrary data supplied by the drag source
20232          */
20233         "enter" : true,
20234         
20235          /**
20236          * @event over
20237          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20238          * This method will be called on every mouse movement while the drag source is over the drop target.
20239          * This default implementation simply returns the dropAllowed config value.
20240          * 
20241          * IMPORTANT : it should set this.dropAllowed
20242          * 
20243          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20244          * @param {Event} e The event
20245          * @param {Object} data An object containing arbitrary data supplied by the drag source
20246          
20247          */
20248         "over" : true,
20249         /**
20250          * @event out
20251          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20252          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20253          * overClass (if any) from the drop element.
20254          * 
20255          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20256          * @param {Event} e The event
20257          * @param {Object} data An object containing arbitrary data supplied by the drag source
20258          */
20259          "out" : true,
20260          
20261         /**
20262          * @event drop
20263          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20264          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20265          * implementation that does something to process the drop event and returns true so that the drag source's
20266          * repair action does not run.
20267          * 
20268          * IMPORTANT : it should set this.success
20269          * 
20270          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20271          * @param {Event} e The event
20272          * @param {Object} data An object containing arbitrary data supplied by the drag source
20273         */
20274          "drop" : true
20275     });
20276             
20277      
20278     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20279         this.el.dom, 
20280         this.ddGroup || this.group,
20281         {
20282             isTarget: true,
20283             listeners : listeners || {} 
20284            
20285         
20286         }
20287     );
20288
20289 };
20290
20291 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20292     /**
20293      * @cfg {String} overClass
20294      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20295      */
20296      /**
20297      * @cfg {String} ddGroup
20298      * The drag drop group to handle drop events for
20299      */
20300      
20301     /**
20302      * @cfg {String} dropAllowed
20303      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20304      */
20305     dropAllowed : "x-dd-drop-ok",
20306     /**
20307      * @cfg {String} dropNotAllowed
20308      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20309      */
20310     dropNotAllowed : "x-dd-drop-nodrop",
20311     /**
20312      * @cfg {boolean} success
20313      * set this after drop listener.. 
20314      */
20315     success : false,
20316     /**
20317      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20318      * if the drop point is valid for over/enter..
20319      */
20320     valid : false,
20321     // private
20322     isTarget : true,
20323
20324     // private
20325     isNotifyTarget : true,
20326     
20327     /**
20328      * @hide
20329      */
20330     notifyEnter : function(dd, e, data)
20331     {
20332         this.valid = true;
20333         this.fireEvent('enter', dd, e, data);
20334         if(this.overClass){
20335             this.el.addClass(this.overClass);
20336         }
20337         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20338             this.valid ? this.dropAllowed : this.dropNotAllowed
20339         );
20340     },
20341
20342     /**
20343      * @hide
20344      */
20345     notifyOver : function(dd, e, data)
20346     {
20347         this.valid = true;
20348         this.fireEvent('over', dd, e, data);
20349         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20350             this.valid ? this.dropAllowed : this.dropNotAllowed
20351         );
20352     },
20353
20354     /**
20355      * @hide
20356      */
20357     notifyOut : function(dd, e, data)
20358     {
20359         this.fireEvent('out', dd, e, data);
20360         if(this.overClass){
20361             this.el.removeClass(this.overClass);
20362         }
20363     },
20364
20365     /**
20366      * @hide
20367      */
20368     notifyDrop : function(dd, e, data)
20369     {
20370         this.success = false;
20371         this.fireEvent('drop', dd, e, data);
20372         return this.success;
20373     }
20374 });/*
20375  * Based on:
20376  * Ext JS Library 1.1.1
20377  * Copyright(c) 2006-2007, Ext JS, LLC.
20378  *
20379  * Originally Released Under LGPL - original licence link has changed is not relivant.
20380  *
20381  * Fork - LGPL
20382  * <script type="text/javascript">
20383  */
20384
20385
20386 /**
20387  * @class Roo.dd.DragZone
20388  * @extends Roo.dd.DragSource
20389  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20390  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20391  * @constructor
20392  * @param {String/HTMLElement/Element} el The container element
20393  * @param {Object} config
20394  */
20395 Roo.dd.DragZone = function(el, config){
20396     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20397     if(this.containerScroll){
20398         Roo.dd.ScrollManager.register(this.el);
20399     }
20400 };
20401
20402 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20403     /**
20404      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20405      * for auto scrolling during drag operations.
20406      */
20407     /**
20408      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20409      * method after a failed drop (defaults to "c3daf9" - light blue)
20410      */
20411
20412     /**
20413      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20414      * for a valid target to drag based on the mouse down. Override this method
20415      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20416      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20417      * @param {EventObject} e The mouse down event
20418      * @return {Object} The dragData
20419      */
20420     getDragData : function(e){
20421         return Roo.dd.Registry.getHandleFromEvent(e);
20422     },
20423     
20424     /**
20425      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20426      * this.dragData.ddel
20427      * @param {Number} x The x position of the click on the dragged object
20428      * @param {Number} y The y position of the click on the dragged object
20429      * @return {Boolean} true to continue the drag, false to cancel
20430      */
20431     onInitDrag : function(x, y){
20432         this.proxy.update(this.dragData.ddel.cloneNode(true));
20433         this.onStartDrag(x, y);
20434         return true;
20435     },
20436     
20437     /**
20438      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20439      */
20440     afterRepair : function(){
20441         if(Roo.enableFx){
20442             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20443         }
20444         this.dragging = false;
20445     },
20446
20447     /**
20448      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20449      * the XY of this.dragData.ddel
20450      * @param {EventObject} e The mouse up event
20451      * @return {Array} The xy location (e.g. [100, 200])
20452      */
20453     getRepairXY : function(e){
20454         return Roo.Element.fly(this.dragData.ddel).getXY();  
20455     }
20456 });/*
20457  * Based on:
20458  * Ext JS Library 1.1.1
20459  * Copyright(c) 2006-2007, Ext JS, LLC.
20460  *
20461  * Originally Released Under LGPL - original licence link has changed is not relivant.
20462  *
20463  * Fork - LGPL
20464  * <script type="text/javascript">
20465  */
20466 /**
20467  * @class Roo.dd.DropZone
20468  * @extends Roo.dd.DropTarget
20469  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20470  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20471  * @constructor
20472  * @param {String/HTMLElement/Element} el The container element
20473  * @param {Object} config
20474  */
20475 Roo.dd.DropZone = function(el, config){
20476     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20477 };
20478
20479 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20480     /**
20481      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20482      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20483      * provide your own custom lookup.
20484      * @param {Event} e The event
20485      * @return {Object} data The custom data
20486      */
20487     getTargetFromEvent : function(e){
20488         return Roo.dd.Registry.getTargetFromEvent(e);
20489     },
20490
20491     /**
20492      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20493      * that it has registered.  This method has no default implementation and should be overridden to provide
20494      * node-specific processing if necessary.
20495      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20496      * {@link #getTargetFromEvent} for this node)
20497      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20498      * @param {Event} e The event
20499      * @param {Object} data An object containing arbitrary data supplied by the drag source
20500      */
20501     onNodeEnter : function(n, dd, e, data){
20502         
20503     },
20504
20505     /**
20506      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20507      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20508      * overridden to provide the proper feedback.
20509      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20510      * {@link #getTargetFromEvent} for this node)
20511      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20512      * @param {Event} e The event
20513      * @param {Object} data An object containing arbitrary data supplied by the drag source
20514      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20515      * underlying {@link Roo.dd.StatusProxy} can be updated
20516      */
20517     onNodeOver : function(n, dd, e, data){
20518         return this.dropAllowed;
20519     },
20520
20521     /**
20522      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20523      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20524      * node-specific processing if necessary.
20525      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20526      * {@link #getTargetFromEvent} for this node)
20527      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20528      * @param {Event} e The event
20529      * @param {Object} data An object containing arbitrary data supplied by the drag source
20530      */
20531     onNodeOut : function(n, dd, e, data){
20532         
20533     },
20534
20535     /**
20536      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20537      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20538      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20539      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20540      * {@link #getTargetFromEvent} for this node)
20541      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20542      * @param {Event} e The event
20543      * @param {Object} data An object containing arbitrary data supplied by the drag source
20544      * @return {Boolean} True if the drop was valid, else false
20545      */
20546     onNodeDrop : function(n, dd, e, data){
20547         return false;
20548     },
20549
20550     /**
20551      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20552      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20553      * it should be overridden to provide the proper feedback if necessary.
20554      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20555      * @param {Event} e The event
20556      * @param {Object} data An object containing arbitrary data supplied by the drag source
20557      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20558      * underlying {@link Roo.dd.StatusProxy} can be updated
20559      */
20560     onContainerOver : function(dd, e, data){
20561         return this.dropNotAllowed;
20562     },
20563
20564     /**
20565      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20566      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20567      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20568      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20569      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20570      * @param {Event} e The event
20571      * @param {Object} data An object containing arbitrary data supplied by the drag source
20572      * @return {Boolean} True if the drop was valid, else false
20573      */
20574     onContainerDrop : function(dd, e, data){
20575         return false;
20576     },
20577
20578     /**
20579      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20580      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20581      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20582      * you should override this method and provide a custom implementation.
20583      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20584      * @param {Event} e The event
20585      * @param {Object} data An object containing arbitrary data supplied by the drag source
20586      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20587      * underlying {@link Roo.dd.StatusProxy} can be updated
20588      */
20589     notifyEnter : function(dd, e, data){
20590         return this.dropNotAllowed;
20591     },
20592
20593     /**
20594      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20595      * This method will be called on every mouse movement while the drag source is over the drop zone.
20596      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20597      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20598      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20599      * registered node, it will call {@link #onContainerOver}.
20600      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20601      * @param {Event} e The event
20602      * @param {Object} data An object containing arbitrary data supplied by the drag source
20603      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20604      * underlying {@link Roo.dd.StatusProxy} can be updated
20605      */
20606     notifyOver : function(dd, e, data){
20607         var n = this.getTargetFromEvent(e);
20608         if(!n){ // not over valid drop target
20609             if(this.lastOverNode){
20610                 this.onNodeOut(this.lastOverNode, dd, e, data);
20611                 this.lastOverNode = null;
20612             }
20613             return this.onContainerOver(dd, e, data);
20614         }
20615         if(this.lastOverNode != n){
20616             if(this.lastOverNode){
20617                 this.onNodeOut(this.lastOverNode, dd, e, data);
20618             }
20619             this.onNodeEnter(n, dd, e, data);
20620             this.lastOverNode = n;
20621         }
20622         return this.onNodeOver(n, dd, e, data);
20623     },
20624
20625     /**
20626      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20627      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20628      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20629      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20630      * @param {Event} e The event
20631      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20632      */
20633     notifyOut : function(dd, e, data){
20634         if(this.lastOverNode){
20635             this.onNodeOut(this.lastOverNode, dd, e, data);
20636             this.lastOverNode = null;
20637         }
20638     },
20639
20640     /**
20641      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20642      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20643      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20644      * otherwise it will call {@link #onContainerDrop}.
20645      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20646      * @param {Event} e The event
20647      * @param {Object} data An object containing arbitrary data supplied by the drag source
20648      * @return {Boolean} True if the drop was valid, else false
20649      */
20650     notifyDrop : function(dd, e, data){
20651         if(this.lastOverNode){
20652             this.onNodeOut(this.lastOverNode, dd, e, data);
20653             this.lastOverNode = null;
20654         }
20655         var n = this.getTargetFromEvent(e);
20656         return n ?
20657             this.onNodeDrop(n, dd, e, data) :
20658             this.onContainerDrop(dd, e, data);
20659     },
20660
20661     // private
20662     triggerCacheRefresh : function(){
20663         Roo.dd.DDM.refreshCache(this.groups);
20664     }  
20665 });/*
20666  * Based on:
20667  * Ext JS Library 1.1.1
20668  * Copyright(c) 2006-2007, Ext JS, LLC.
20669  *
20670  * Originally Released Under LGPL - original licence link has changed is not relivant.
20671  *
20672  * Fork - LGPL
20673  * <script type="text/javascript">
20674  */
20675
20676
20677 /**
20678  * @class Roo.data.SortTypes
20679  * @singleton
20680  * Defines the default sorting (casting?) comparison functions used when sorting data.
20681  */
20682 Roo.data.SortTypes = {
20683     /**
20684      * Default sort that does nothing
20685      * @param {Mixed} s The value being converted
20686      * @return {Mixed} The comparison value
20687      */
20688     none : function(s){
20689         return s;
20690     },
20691     
20692     /**
20693      * The regular expression used to strip tags
20694      * @type {RegExp}
20695      * @property
20696      */
20697     stripTagsRE : /<\/?[^>]+>/gi,
20698     
20699     /**
20700      * Strips all HTML tags to sort on text only
20701      * @param {Mixed} s The value being converted
20702      * @return {String} The comparison value
20703      */
20704     asText : function(s){
20705         return String(s).replace(this.stripTagsRE, "");
20706     },
20707     
20708     /**
20709      * Strips all HTML tags to sort on text only - Case insensitive
20710      * @param {Mixed} s The value being converted
20711      * @return {String} The comparison value
20712      */
20713     asUCText : function(s){
20714         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20715     },
20716     
20717     /**
20718      * Case insensitive string
20719      * @param {Mixed} s The value being converted
20720      * @return {String} The comparison value
20721      */
20722     asUCString : function(s) {
20723         return String(s).toUpperCase();
20724     },
20725     
20726     /**
20727      * Date sorting
20728      * @param {Mixed} s The value being converted
20729      * @return {Number} The comparison value
20730      */
20731     asDate : function(s) {
20732         if(!s){
20733             return 0;
20734         }
20735         if(s instanceof Date){
20736             return s.getTime();
20737         }
20738         return Date.parse(String(s));
20739     },
20740     
20741     /**
20742      * Float sorting
20743      * @param {Mixed} s The value being converted
20744      * @return {Float} The comparison value
20745      */
20746     asFloat : function(s) {
20747         var val = parseFloat(String(s).replace(/,/g, ""));
20748         if(isNaN(val)) val = 0;
20749         return val;
20750     },
20751     
20752     /**
20753      * Integer sorting
20754      * @param {Mixed} s The value being converted
20755      * @return {Number} The comparison value
20756      */
20757     asInt : function(s) {
20758         var val = parseInt(String(s).replace(/,/g, ""));
20759         if(isNaN(val)) val = 0;
20760         return val;
20761     }
20762 };/*
20763  * Based on:
20764  * Ext JS Library 1.1.1
20765  * Copyright(c) 2006-2007, Ext JS, LLC.
20766  *
20767  * Originally Released Under LGPL - original licence link has changed is not relivant.
20768  *
20769  * Fork - LGPL
20770  * <script type="text/javascript">
20771  */
20772
20773 /**
20774 * @class Roo.data.Record
20775  * Instances of this class encapsulate both record <em>definition</em> information, and record
20776  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20777  * to access Records cached in an {@link Roo.data.Store} object.<br>
20778  * <p>
20779  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20780  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20781  * objects.<br>
20782  * <p>
20783  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20784  * @constructor
20785  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20786  * {@link #create}. The parameters are the same.
20787  * @param {Array} data An associative Array of data values keyed by the field name.
20788  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20789  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20790  * not specified an integer id is generated.
20791  */
20792 Roo.data.Record = function(data, id){
20793     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20794     this.data = data;
20795 };
20796
20797 /**
20798  * Generate a constructor for a specific record layout.
20799  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20800  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20801  * Each field definition object may contain the following properties: <ul>
20802  * <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,
20803  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20804  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20805  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20806  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20807  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20808  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20809  * this may be omitted.</p></li>
20810  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20811  * <ul><li>auto (Default, implies no conversion)</li>
20812  * <li>string</li>
20813  * <li>int</li>
20814  * <li>float</li>
20815  * <li>boolean</li>
20816  * <li>date</li></ul></p></li>
20817  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20818  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20819  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20820  * by the Reader into an object that will be stored in the Record. It is passed the
20821  * following parameters:<ul>
20822  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20823  * </ul></p></li>
20824  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20825  * </ul>
20826  * <br>usage:<br><pre><code>
20827 var TopicRecord = Roo.data.Record.create(
20828     {name: 'title', mapping: 'topic_title'},
20829     {name: 'author', mapping: 'username'},
20830     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20831     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20832     {name: 'lastPoster', mapping: 'user2'},
20833     {name: 'excerpt', mapping: 'post_text'}
20834 );
20835
20836 var myNewRecord = new TopicRecord({
20837     title: 'Do my job please',
20838     author: 'noobie',
20839     totalPosts: 1,
20840     lastPost: new Date(),
20841     lastPoster: 'Animal',
20842     excerpt: 'No way dude!'
20843 });
20844 myStore.add(myNewRecord);
20845 </code></pre>
20846  * @method create
20847  * @static
20848  */
20849 Roo.data.Record.create = function(o){
20850     var f = function(){
20851         f.superclass.constructor.apply(this, arguments);
20852     };
20853     Roo.extend(f, Roo.data.Record);
20854     var p = f.prototype;
20855     p.fields = new Roo.util.MixedCollection(false, function(field){
20856         return field.name;
20857     });
20858     for(var i = 0, len = o.length; i < len; i++){
20859         p.fields.add(new Roo.data.Field(o[i]));
20860     }
20861     f.getField = function(name){
20862         return p.fields.get(name);  
20863     };
20864     return f;
20865 };
20866
20867 Roo.data.Record.AUTO_ID = 1000;
20868 Roo.data.Record.EDIT = 'edit';
20869 Roo.data.Record.REJECT = 'reject';
20870 Roo.data.Record.COMMIT = 'commit';
20871
20872 Roo.data.Record.prototype = {
20873     /**
20874      * Readonly flag - true if this record has been modified.
20875      * @type Boolean
20876      */
20877     dirty : false,
20878     editing : false,
20879     error: null,
20880     modified: null,
20881
20882     // private
20883     join : function(store){
20884         this.store = store;
20885     },
20886
20887     /**
20888      * Set the named field to the specified value.
20889      * @param {String} name The name of the field to set.
20890      * @param {Object} value The value to set the field to.
20891      */
20892     set : function(name, value){
20893         if(this.data[name] == value){
20894             return;
20895         }
20896         this.dirty = true;
20897         if(!this.modified){
20898             this.modified = {};
20899         }
20900         if(typeof this.modified[name] == 'undefined'){
20901             this.modified[name] = this.data[name];
20902         }
20903         this.data[name] = value;
20904         if(!this.editing && this.store){
20905             this.store.afterEdit(this);
20906         }       
20907     },
20908
20909     /**
20910      * Get the value of the named field.
20911      * @param {String} name The name of the field to get the value of.
20912      * @return {Object} The value of the field.
20913      */
20914     get : function(name){
20915         return this.data[name]; 
20916     },
20917
20918     // private
20919     beginEdit : function(){
20920         this.editing = true;
20921         this.modified = {}; 
20922     },
20923
20924     // private
20925     cancelEdit : function(){
20926         this.editing = false;
20927         delete this.modified;
20928     },
20929
20930     // private
20931     endEdit : function(){
20932         this.editing = false;
20933         if(this.dirty && this.store){
20934             this.store.afterEdit(this);
20935         }
20936     },
20937
20938     /**
20939      * Usually called by the {@link Roo.data.Store} which owns the Record.
20940      * Rejects all changes made to the Record since either creation, or the last commit operation.
20941      * Modified fields are reverted to their original values.
20942      * <p>
20943      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20944      * of reject operations.
20945      */
20946     reject : function(){
20947         var m = this.modified;
20948         for(var n in m){
20949             if(typeof m[n] != "function"){
20950                 this.data[n] = m[n];
20951             }
20952         }
20953         this.dirty = false;
20954         delete this.modified;
20955         this.editing = false;
20956         if(this.store){
20957             this.store.afterReject(this);
20958         }
20959     },
20960
20961     /**
20962      * Usually called by the {@link Roo.data.Store} which owns the Record.
20963      * Commits all changes made to the Record since either creation, or the last commit operation.
20964      * <p>
20965      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20966      * of commit operations.
20967      */
20968     commit : function(){
20969         this.dirty = false;
20970         delete this.modified;
20971         this.editing = false;
20972         if(this.store){
20973             this.store.afterCommit(this);
20974         }
20975     },
20976
20977     // private
20978     hasError : function(){
20979         return this.error != null;
20980     },
20981
20982     // private
20983     clearError : function(){
20984         this.error = null;
20985     },
20986
20987     /**
20988      * Creates a copy of this record.
20989      * @param {String} id (optional) A new record id if you don't want to use this record's id
20990      * @return {Record}
20991      */
20992     copy : function(newId) {
20993         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
20994     }
20995 };/*
20996  * Based on:
20997  * Ext JS Library 1.1.1
20998  * Copyright(c) 2006-2007, Ext JS, LLC.
20999  *
21000  * Originally Released Under LGPL - original licence link has changed is not relivant.
21001  *
21002  * Fork - LGPL
21003  * <script type="text/javascript">
21004  */
21005
21006
21007
21008 /**
21009  * @class Roo.data.Store
21010  * @extends Roo.util.Observable
21011  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21012  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21013  * <p>
21014  * 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
21015  * has no knowledge of the format of the data returned by the Proxy.<br>
21016  * <p>
21017  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21018  * instances from the data object. These records are cached and made available through accessor functions.
21019  * @constructor
21020  * Creates a new Store.
21021  * @param {Object} config A config object containing the objects needed for the Store to access data,
21022  * and read the data into Records.
21023  */
21024 Roo.data.Store = function(config){
21025     this.data = new Roo.util.MixedCollection(false);
21026     this.data.getKey = function(o){
21027         return o.id;
21028     };
21029     this.baseParams = {};
21030     // private
21031     this.paramNames = {
21032         "start" : "start",
21033         "limit" : "limit",
21034         "sort" : "sort",
21035         "dir" : "dir",
21036         "multisort" : "_multisort"
21037     };
21038
21039     if(config && config.data){
21040         this.inlineData = config.data;
21041         delete config.data;
21042     }
21043
21044     Roo.apply(this, config);
21045     
21046     if(this.reader){ // reader passed
21047         this.reader = Roo.factory(this.reader, Roo.data);
21048         this.reader.xmodule = this.xmodule || false;
21049         if(!this.recordType){
21050             this.recordType = this.reader.recordType;
21051         }
21052         if(this.reader.onMetaChange){
21053             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21054         }
21055     }
21056
21057     if(this.recordType){
21058         this.fields = this.recordType.prototype.fields;
21059     }
21060     this.modified = [];
21061
21062     this.addEvents({
21063         /**
21064          * @event datachanged
21065          * Fires when the data cache has changed, and a widget which is using this Store
21066          * as a Record cache should refresh its view.
21067          * @param {Store} this
21068          */
21069         datachanged : true,
21070         /**
21071          * @event metachange
21072          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21073          * @param {Store} this
21074          * @param {Object} meta The JSON metadata
21075          */
21076         metachange : true,
21077         /**
21078          * @event add
21079          * Fires when Records have been added to the Store
21080          * @param {Store} this
21081          * @param {Roo.data.Record[]} records The array of Records added
21082          * @param {Number} index The index at which the record(s) were added
21083          */
21084         add : true,
21085         /**
21086          * @event remove
21087          * Fires when a Record has been removed from the Store
21088          * @param {Store} this
21089          * @param {Roo.data.Record} record The Record that was removed
21090          * @param {Number} index The index at which the record was removed
21091          */
21092         remove : true,
21093         /**
21094          * @event update
21095          * Fires when a Record has been updated
21096          * @param {Store} this
21097          * @param {Roo.data.Record} record The Record that was updated
21098          * @param {String} operation The update operation being performed.  Value may be one of:
21099          * <pre><code>
21100  Roo.data.Record.EDIT
21101  Roo.data.Record.REJECT
21102  Roo.data.Record.COMMIT
21103          * </code></pre>
21104          */
21105         update : true,
21106         /**
21107          * @event clear
21108          * Fires when the data cache has been cleared.
21109          * @param {Store} this
21110          */
21111         clear : true,
21112         /**
21113          * @event beforeload
21114          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21115          * the load action will be canceled.
21116          * @param {Store} this
21117          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21118          */
21119         beforeload : true,
21120         /**
21121          * @event beforeloadadd
21122          * Fires after a new set of Records has been loaded.
21123          * @param {Store} this
21124          * @param {Roo.data.Record[]} records The Records that were loaded
21125          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21126          */
21127         beforeloadadd : true,
21128         /**
21129          * @event load
21130          * Fires after a new set of Records has been loaded, before they are added to the store.
21131          * @param {Store} this
21132          * @param {Roo.data.Record[]} records The Records that were loaded
21133          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21134          * @params {Object} return from reader
21135          */
21136         load : true,
21137         /**
21138          * @event loadexception
21139          * Fires if an exception occurs in the Proxy during loading.
21140          * Called with the signature of the Proxy's "loadexception" event.
21141          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21142          * 
21143          * @param {Proxy} 
21144          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21145          * @param {Object} load options 
21146          * @param {Object} jsonData from your request (normally this contains the Exception)
21147          */
21148         loadexception : true
21149     });
21150     
21151     if(this.proxy){
21152         this.proxy = Roo.factory(this.proxy, Roo.data);
21153         this.proxy.xmodule = this.xmodule || false;
21154         this.relayEvents(this.proxy,  ["loadexception"]);
21155     }
21156     this.sortToggle = {};
21157     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21158
21159     Roo.data.Store.superclass.constructor.call(this);
21160
21161     if(this.inlineData){
21162         this.loadData(this.inlineData);
21163         delete this.inlineData;
21164     }
21165 };
21166
21167 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21168      /**
21169     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21170     * without a remote query - used by combo/forms at present.
21171     */
21172     
21173     /**
21174     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21175     */
21176     /**
21177     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21178     */
21179     /**
21180     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21181     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21182     */
21183     /**
21184     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21185     * on any HTTP request
21186     */
21187     /**
21188     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21189     */
21190     /**
21191     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21192     */
21193     multiSort: false,
21194     /**
21195     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21196     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21197     */
21198     remoteSort : false,
21199
21200     /**
21201     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21202      * loaded or when a record is removed. (defaults to false).
21203     */
21204     pruneModifiedRecords : false,
21205
21206     // private
21207     lastOptions : null,
21208
21209     /**
21210      * Add Records to the Store and fires the add event.
21211      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21212      */
21213     add : function(records){
21214         records = [].concat(records);
21215         for(var i = 0, len = records.length; i < len; i++){
21216             records[i].join(this);
21217         }
21218         var index = this.data.length;
21219         this.data.addAll(records);
21220         this.fireEvent("add", this, records, index);
21221     },
21222
21223     /**
21224      * Remove a Record from the Store and fires the remove event.
21225      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21226      */
21227     remove : function(record){
21228         var index = this.data.indexOf(record);
21229         this.data.removeAt(index);
21230         if(this.pruneModifiedRecords){
21231             this.modified.remove(record);
21232         }
21233         this.fireEvent("remove", this, record, index);
21234     },
21235
21236     /**
21237      * Remove all Records from the Store and fires the clear event.
21238      */
21239     removeAll : function(){
21240         this.data.clear();
21241         if(this.pruneModifiedRecords){
21242             this.modified = [];
21243         }
21244         this.fireEvent("clear", this);
21245     },
21246
21247     /**
21248      * Inserts Records to the Store at the given index and fires the add event.
21249      * @param {Number} index The start index at which to insert the passed Records.
21250      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21251      */
21252     insert : function(index, records){
21253         records = [].concat(records);
21254         for(var i = 0, len = records.length; i < len; i++){
21255             this.data.insert(index, records[i]);
21256             records[i].join(this);
21257         }
21258         this.fireEvent("add", this, records, index);
21259     },
21260
21261     /**
21262      * Get the index within the cache of the passed Record.
21263      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21264      * @return {Number} The index of the passed Record. Returns -1 if not found.
21265      */
21266     indexOf : function(record){
21267         return this.data.indexOf(record);
21268     },
21269
21270     /**
21271      * Get the index within the cache of the Record with the passed id.
21272      * @param {String} id The id of the Record to find.
21273      * @return {Number} The index of the Record. Returns -1 if not found.
21274      */
21275     indexOfId : function(id){
21276         return this.data.indexOfKey(id);
21277     },
21278
21279     /**
21280      * Get the Record with the specified id.
21281      * @param {String} id The id of the Record to find.
21282      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21283      */
21284     getById : function(id){
21285         return this.data.key(id);
21286     },
21287
21288     /**
21289      * Get the Record at the specified index.
21290      * @param {Number} index The index of the Record to find.
21291      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21292      */
21293     getAt : function(index){
21294         return this.data.itemAt(index);
21295     },
21296
21297     /**
21298      * Returns a range of Records between specified indices.
21299      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21300      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21301      * @return {Roo.data.Record[]} An array of Records
21302      */
21303     getRange : function(start, end){
21304         return this.data.getRange(start, end);
21305     },
21306
21307     // private
21308     storeOptions : function(o){
21309         o = Roo.apply({}, o);
21310         delete o.callback;
21311         delete o.scope;
21312         this.lastOptions = o;
21313     },
21314
21315     /**
21316      * Loads the Record cache from the configured Proxy using the configured Reader.
21317      * <p>
21318      * If using remote paging, then the first load call must specify the <em>start</em>
21319      * and <em>limit</em> properties in the options.params property to establish the initial
21320      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21321      * <p>
21322      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21323      * and this call will return before the new data has been loaded. Perform any post-processing
21324      * in a callback function, or in a "load" event handler.</strong>
21325      * <p>
21326      * @param {Object} options An object containing properties which control loading options:<ul>
21327      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21328      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21329      * passed the following arguments:<ul>
21330      * <li>r : Roo.data.Record[]</li>
21331      * <li>options: Options object from the load call</li>
21332      * <li>success: Boolean success indicator</li></ul></li>
21333      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21334      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21335      * </ul>
21336      */
21337     load : function(options){
21338         options = options || {};
21339         if(this.fireEvent("beforeload", this, options) !== false){
21340             this.storeOptions(options);
21341             var p = Roo.apply(options.params || {}, this.baseParams);
21342             // if meta was not loaded from remote source.. try requesting it.
21343             if (!this.reader.metaFromRemote) {
21344                 p._requestMeta = 1;
21345             }
21346             if(this.sortInfo && this.remoteSort){
21347                 var pn = this.paramNames;
21348                 p[pn["sort"]] = this.sortInfo.field;
21349                 p[pn["dir"]] = this.sortInfo.direction;
21350             }
21351             if (this.multiSort) {
21352                 var pn = this.paramNames;
21353                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21354             }
21355             
21356             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21357         }
21358     },
21359
21360     /**
21361      * Reloads the Record cache from the configured Proxy using the configured Reader and
21362      * the options from the last load operation performed.
21363      * @param {Object} options (optional) An object containing properties which may override the options
21364      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21365      * the most recently used options are reused).
21366      */
21367     reload : function(options){
21368         this.load(Roo.applyIf(options||{}, this.lastOptions));
21369     },
21370
21371     // private
21372     // Called as a callback by the Reader during a load operation.
21373     loadRecords : function(o, options, success){
21374         if(!o || success === false){
21375             if(success !== false){
21376                 this.fireEvent("load", this, [], options, o);
21377             }
21378             if(options.callback){
21379                 options.callback.call(options.scope || this, [], options, false);
21380             }
21381             return;
21382         }
21383         // if data returned failure - throw an exception.
21384         if (o.success === false) {
21385             // show a message if no listener is registered.
21386             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21387                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21388             }
21389             // loadmask wil be hooked into this..
21390             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21391             return;
21392         }
21393         var r = o.records, t = o.totalRecords || r.length;
21394         
21395         this.fireEvent("beforeloadadd", this, r, options, o);
21396         
21397         if(!options || options.add !== true){
21398             if(this.pruneModifiedRecords){
21399                 this.modified = [];
21400             }
21401             for(var i = 0, len = r.length; i < len; i++){
21402                 r[i].join(this);
21403             }
21404             if(this.snapshot){
21405                 this.data = this.snapshot;
21406                 delete this.snapshot;
21407             }
21408             this.data.clear();
21409             this.data.addAll(r);
21410             this.totalLength = t;
21411             this.applySort();
21412             this.fireEvent("datachanged", this);
21413         }else{
21414             this.totalLength = Math.max(t, this.data.length+r.length);
21415             this.add(r);
21416         }
21417         this.fireEvent("load", this, r, options, o);
21418         if(options.callback){
21419             options.callback.call(options.scope || this, r, options, true);
21420         }
21421     },
21422
21423
21424     /**
21425      * Loads data from a passed data block. A Reader which understands the format of the data
21426      * must have been configured in the constructor.
21427      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21428      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21429      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21430      */
21431     loadData : function(o, append){
21432         var r = this.reader.readRecords(o);
21433         this.loadRecords(r, {add: append}, true);
21434     },
21435
21436     /**
21437      * Gets the number of cached records.
21438      * <p>
21439      * <em>If using paging, this may not be the total size of the dataset. If the data object
21440      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21441      * the data set size</em>
21442      */
21443     getCount : function(){
21444         return this.data.length || 0;
21445     },
21446
21447     /**
21448      * Gets the total number of records in the dataset as returned by the server.
21449      * <p>
21450      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21451      * the dataset size</em>
21452      */
21453     getTotalCount : function(){
21454         return this.totalLength || 0;
21455     },
21456
21457     /**
21458      * Returns the sort state of the Store as an object with two properties:
21459      * <pre><code>
21460  field {String} The name of the field by which the Records are sorted
21461  direction {String} The sort order, "ASC" or "DESC"
21462      * </code></pre>
21463      */
21464     getSortState : function(){
21465         return this.sortInfo;
21466     },
21467
21468     // private
21469     applySort : function(){
21470         if(this.sortInfo && !this.remoteSort){
21471             var s = this.sortInfo, f = s.field;
21472             var st = this.fields.get(f).sortType;
21473             var fn = function(r1, r2){
21474                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21475                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21476             };
21477             this.data.sort(s.direction, fn);
21478             if(this.snapshot && this.snapshot != this.data){
21479                 this.snapshot.sort(s.direction, fn);
21480             }
21481         }
21482     },
21483
21484     /**
21485      * Sets the default sort column and order to be used by the next load operation.
21486      * @param {String} fieldName The name of the field to sort by.
21487      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21488      */
21489     setDefaultSort : function(field, dir){
21490         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21491     },
21492
21493     /**
21494      * Sort the Records.
21495      * If remote sorting is used, the sort is performed on the server, and the cache is
21496      * reloaded. If local sorting is used, the cache is sorted internally.
21497      * @param {String} fieldName The name of the field to sort by.
21498      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21499      */
21500     sort : function(fieldName, dir){
21501         var f = this.fields.get(fieldName);
21502         if(!dir){
21503             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21504             
21505             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21506                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21507             }else{
21508                 dir = f.sortDir;
21509             }
21510         }
21511         this.sortToggle[f.name] = dir;
21512         this.sortInfo = {field: f.name, direction: dir};
21513         if(!this.remoteSort){
21514             this.applySort();
21515             this.fireEvent("datachanged", this);
21516         }else{
21517             this.load(this.lastOptions);
21518         }
21519     },
21520
21521     /**
21522      * Calls the specified function for each of the Records in the cache.
21523      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21524      * Returning <em>false</em> aborts and exits the iteration.
21525      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21526      */
21527     each : function(fn, scope){
21528         this.data.each(fn, scope);
21529     },
21530
21531     /**
21532      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21533      * (e.g., during paging).
21534      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21535      */
21536     getModifiedRecords : function(){
21537         return this.modified;
21538     },
21539
21540     // private
21541     createFilterFn : function(property, value, anyMatch){
21542         if(!value.exec){ // not a regex
21543             value = String(value);
21544             if(value.length == 0){
21545                 return false;
21546             }
21547             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21548         }
21549         return function(r){
21550             return value.test(r.data[property]);
21551         };
21552     },
21553
21554     /**
21555      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21556      * @param {String} property A field on your records
21557      * @param {Number} start The record index to start at (defaults to 0)
21558      * @param {Number} end The last record index to include (defaults to length - 1)
21559      * @return {Number} The sum
21560      */
21561     sum : function(property, start, end){
21562         var rs = this.data.items, v = 0;
21563         start = start || 0;
21564         end = (end || end === 0) ? end : rs.length-1;
21565
21566         for(var i = start; i <= end; i++){
21567             v += (rs[i].data[property] || 0);
21568         }
21569         return v;
21570     },
21571
21572     /**
21573      * Filter the records by a specified property.
21574      * @param {String} field A field on your records
21575      * @param {String/RegExp} value Either a string that the field
21576      * should start with or a RegExp to test against the field
21577      * @param {Boolean} anyMatch True to match any part not just the beginning
21578      */
21579     filter : function(property, value, anyMatch){
21580         var fn = this.createFilterFn(property, value, anyMatch);
21581         return fn ? this.filterBy(fn) : this.clearFilter();
21582     },
21583
21584     /**
21585      * Filter by a function. The specified function will be called with each
21586      * record in this data source. If the function returns true the record is included,
21587      * otherwise it is filtered.
21588      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21589      * @param {Object} scope (optional) The scope of the function (defaults to this)
21590      */
21591     filterBy : function(fn, scope){
21592         this.snapshot = this.snapshot || this.data;
21593         this.data = this.queryBy(fn, scope||this);
21594         this.fireEvent("datachanged", this);
21595     },
21596
21597     /**
21598      * Query the records by a specified property.
21599      * @param {String} field A field on your records
21600      * @param {String/RegExp} value Either a string that the field
21601      * should start with or a RegExp to test against the field
21602      * @param {Boolean} anyMatch True to match any part not just the beginning
21603      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21604      */
21605     query : function(property, value, anyMatch){
21606         var fn = this.createFilterFn(property, value, anyMatch);
21607         return fn ? this.queryBy(fn) : this.data.clone();
21608     },
21609
21610     /**
21611      * Query by a function. The specified function will be called with each
21612      * record in this data source. If the function returns true the record is included
21613      * in the results.
21614      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21615      * @param {Object} scope (optional) The scope of the function (defaults to this)
21616       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21617      **/
21618     queryBy : function(fn, scope){
21619         var data = this.snapshot || this.data;
21620         return data.filterBy(fn, scope||this);
21621     },
21622
21623     /**
21624      * Collects unique values for a particular dataIndex from this store.
21625      * @param {String} dataIndex The property to collect
21626      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21627      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21628      * @return {Array} An array of the unique values
21629      **/
21630     collect : function(dataIndex, allowNull, bypassFilter){
21631         var d = (bypassFilter === true && this.snapshot) ?
21632                 this.snapshot.items : this.data.items;
21633         var v, sv, r = [], l = {};
21634         for(var i = 0, len = d.length; i < len; i++){
21635             v = d[i].data[dataIndex];
21636             sv = String(v);
21637             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21638                 l[sv] = true;
21639                 r[r.length] = v;
21640             }
21641         }
21642         return r;
21643     },
21644
21645     /**
21646      * Revert to a view of the Record cache with no filtering applied.
21647      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21648      */
21649     clearFilter : function(suppressEvent){
21650         if(this.snapshot && this.snapshot != this.data){
21651             this.data = this.snapshot;
21652             delete this.snapshot;
21653             if(suppressEvent !== true){
21654                 this.fireEvent("datachanged", this);
21655             }
21656         }
21657     },
21658
21659     // private
21660     afterEdit : function(record){
21661         if(this.modified.indexOf(record) == -1){
21662             this.modified.push(record);
21663         }
21664         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21665     },
21666     
21667     // private
21668     afterReject : function(record){
21669         this.modified.remove(record);
21670         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21671     },
21672
21673     // private
21674     afterCommit : function(record){
21675         this.modified.remove(record);
21676         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21677     },
21678
21679     /**
21680      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21681      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21682      */
21683     commitChanges : function(){
21684         var m = this.modified.slice(0);
21685         this.modified = [];
21686         for(var i = 0, len = m.length; i < len; i++){
21687             m[i].commit();
21688         }
21689     },
21690
21691     /**
21692      * Cancel outstanding changes on all changed records.
21693      */
21694     rejectChanges : function(){
21695         var m = this.modified.slice(0);
21696         this.modified = [];
21697         for(var i = 0, len = m.length; i < len; i++){
21698             m[i].reject();
21699         }
21700     },
21701
21702     onMetaChange : function(meta, rtype, o){
21703         this.recordType = rtype;
21704         this.fields = rtype.prototype.fields;
21705         delete this.snapshot;
21706         this.sortInfo = meta.sortInfo || this.sortInfo;
21707         this.modified = [];
21708         this.fireEvent('metachange', this, this.reader.meta);
21709     }
21710 });/*
21711  * Based on:
21712  * Ext JS Library 1.1.1
21713  * Copyright(c) 2006-2007, Ext JS, LLC.
21714  *
21715  * Originally Released Under LGPL - original licence link has changed is not relivant.
21716  *
21717  * Fork - LGPL
21718  * <script type="text/javascript">
21719  */
21720
21721 /**
21722  * @class Roo.data.SimpleStore
21723  * @extends Roo.data.Store
21724  * Small helper class to make creating Stores from Array data easier.
21725  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21726  * @cfg {Array} fields An array of field definition objects, or field name strings.
21727  * @cfg {Array} data The multi-dimensional array of data
21728  * @constructor
21729  * @param {Object} config
21730  */
21731 Roo.data.SimpleStore = function(config){
21732     Roo.data.SimpleStore.superclass.constructor.call(this, {
21733         isLocal : true,
21734         reader: new Roo.data.ArrayReader({
21735                 id: config.id
21736             },
21737             Roo.data.Record.create(config.fields)
21738         ),
21739         proxy : new Roo.data.MemoryProxy(config.data)
21740     });
21741     this.load();
21742 };
21743 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21744  * Based on:
21745  * Ext JS Library 1.1.1
21746  * Copyright(c) 2006-2007, Ext JS, LLC.
21747  *
21748  * Originally Released Under LGPL - original licence link has changed is not relivant.
21749  *
21750  * Fork - LGPL
21751  * <script type="text/javascript">
21752  */
21753
21754 /**
21755 /**
21756  * @extends Roo.data.Store
21757  * @class Roo.data.JsonStore
21758  * Small helper class to make creating Stores for JSON data easier. <br/>
21759 <pre><code>
21760 var store = new Roo.data.JsonStore({
21761     url: 'get-images.php',
21762     root: 'images',
21763     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21764 });
21765 </code></pre>
21766  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21767  * JsonReader and HttpProxy (unless inline data is provided).</b>
21768  * @cfg {Array} fields An array of field definition objects, or field name strings.
21769  * @constructor
21770  * @param {Object} config
21771  */
21772 Roo.data.JsonStore = function(c){
21773     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21774         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21775         reader: new Roo.data.JsonReader(c, c.fields)
21776     }));
21777 };
21778 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21779  * Based on:
21780  * Ext JS Library 1.1.1
21781  * Copyright(c) 2006-2007, Ext JS, LLC.
21782  *
21783  * Originally Released Under LGPL - original licence link has changed is not relivant.
21784  *
21785  * Fork - LGPL
21786  * <script type="text/javascript">
21787  */
21788
21789  
21790 Roo.data.Field = function(config){
21791     if(typeof config == "string"){
21792         config = {name: config};
21793     }
21794     Roo.apply(this, config);
21795     
21796     if(!this.type){
21797         this.type = "auto";
21798     }
21799     
21800     var st = Roo.data.SortTypes;
21801     // named sortTypes are supported, here we look them up
21802     if(typeof this.sortType == "string"){
21803         this.sortType = st[this.sortType];
21804     }
21805     
21806     // set default sortType for strings and dates
21807     if(!this.sortType){
21808         switch(this.type){
21809             case "string":
21810                 this.sortType = st.asUCString;
21811                 break;
21812             case "date":
21813                 this.sortType = st.asDate;
21814                 break;
21815             default:
21816                 this.sortType = st.none;
21817         }
21818     }
21819
21820     // define once
21821     var stripRe = /[\$,%]/g;
21822
21823     // prebuilt conversion function for this field, instead of
21824     // switching every time we're reading a value
21825     if(!this.convert){
21826         var cv, dateFormat = this.dateFormat;
21827         switch(this.type){
21828             case "":
21829             case "auto":
21830             case undefined:
21831                 cv = function(v){ return v; };
21832                 break;
21833             case "string":
21834                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21835                 break;
21836             case "int":
21837                 cv = function(v){
21838                     return v !== undefined && v !== null && v !== '' ?
21839                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21840                     };
21841                 break;
21842             case "float":
21843                 cv = function(v){
21844                     return v !== undefined && v !== null && v !== '' ?
21845                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21846                     };
21847                 break;
21848             case "bool":
21849             case "boolean":
21850                 cv = function(v){ return v === true || v === "true" || v == 1; };
21851                 break;
21852             case "date":
21853                 cv = function(v){
21854                     if(!v){
21855                         return '';
21856                     }
21857                     if(v instanceof Date){
21858                         return v;
21859                     }
21860                     if(dateFormat){
21861                         if(dateFormat == "timestamp"){
21862                             return new Date(v*1000);
21863                         }
21864                         return Date.parseDate(v, dateFormat);
21865                     }
21866                     var parsed = Date.parse(v);
21867                     return parsed ? new Date(parsed) : null;
21868                 };
21869              break;
21870             
21871         }
21872         this.convert = cv;
21873     }
21874 };
21875
21876 Roo.data.Field.prototype = {
21877     dateFormat: null,
21878     defaultValue: "",
21879     mapping: null,
21880     sortType : null,
21881     sortDir : "ASC"
21882 };/*
21883  * Based on:
21884  * Ext JS Library 1.1.1
21885  * Copyright(c) 2006-2007, Ext JS, LLC.
21886  *
21887  * Originally Released Under LGPL - original licence link has changed is not relivant.
21888  *
21889  * Fork - LGPL
21890  * <script type="text/javascript">
21891  */
21892  
21893 // Base class for reading structured data from a data source.  This class is intended to be
21894 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21895
21896 /**
21897  * @class Roo.data.DataReader
21898  * Base class for reading structured data from a data source.  This class is intended to be
21899  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21900  */
21901
21902 Roo.data.DataReader = function(meta, recordType){
21903     
21904     this.meta = meta;
21905     
21906     this.recordType = recordType instanceof Array ? 
21907         Roo.data.Record.create(recordType) : recordType;
21908 };
21909
21910 Roo.data.DataReader.prototype = {
21911      /**
21912      * Create an empty record
21913      * @param {Object} data (optional) - overlay some values
21914      * @return {Roo.data.Record} record created.
21915      */
21916     newRow :  function(d) {
21917         var da =  {};
21918         this.recordType.prototype.fields.each(function(c) {
21919             switch( c.type) {
21920                 case 'int' : da[c.name] = 0; break;
21921                 case 'date' : da[c.name] = new Date(); break;
21922                 case 'float' : da[c.name] = 0.0; break;
21923                 case 'boolean' : da[c.name] = false; break;
21924                 default : da[c.name] = ""; break;
21925             }
21926             
21927         });
21928         return new this.recordType(Roo.apply(da, d));
21929     }
21930     
21931 };/*
21932  * Based on:
21933  * Ext JS Library 1.1.1
21934  * Copyright(c) 2006-2007, Ext JS, LLC.
21935  *
21936  * Originally Released Under LGPL - original licence link has changed is not relivant.
21937  *
21938  * Fork - LGPL
21939  * <script type="text/javascript">
21940  */
21941
21942 /**
21943  * @class Roo.data.DataProxy
21944  * @extends Roo.data.Observable
21945  * This class is an abstract base class for implementations which provide retrieval of
21946  * unformatted data objects.<br>
21947  * <p>
21948  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21949  * (of the appropriate type which knows how to parse the data object) to provide a block of
21950  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21951  * <p>
21952  * Custom implementations must implement the load method as described in
21953  * {@link Roo.data.HttpProxy#load}.
21954  */
21955 Roo.data.DataProxy = function(){
21956     this.addEvents({
21957         /**
21958          * @event beforeload
21959          * Fires before a network request is made to retrieve a data object.
21960          * @param {Object} This DataProxy object.
21961          * @param {Object} params The params parameter to the load function.
21962          */
21963         beforeload : true,
21964         /**
21965          * @event load
21966          * Fires before the load method's callback is called.
21967          * @param {Object} This DataProxy object.
21968          * @param {Object} o The data object.
21969          * @param {Object} arg The callback argument object passed to the load function.
21970          */
21971         load : true,
21972         /**
21973          * @event loadexception
21974          * Fires if an Exception occurs during data retrieval.
21975          * @param {Object} This DataProxy object.
21976          * @param {Object} o The data object.
21977          * @param {Object} arg The callback argument object passed to the load function.
21978          * @param {Object} e The Exception.
21979          */
21980         loadexception : true
21981     });
21982     Roo.data.DataProxy.superclass.constructor.call(this);
21983 };
21984
21985 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
21986
21987     /**
21988      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
21989      */
21990 /*
21991  * Based on:
21992  * Ext JS Library 1.1.1
21993  * Copyright(c) 2006-2007, Ext JS, LLC.
21994  *
21995  * Originally Released Under LGPL - original licence link has changed is not relivant.
21996  *
21997  * Fork - LGPL
21998  * <script type="text/javascript">
21999  */
22000 /**
22001  * @class Roo.data.MemoryProxy
22002  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22003  * to the Reader when its load method is called.
22004  * @constructor
22005  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22006  */
22007 Roo.data.MemoryProxy = function(data){
22008     if (data.data) {
22009         data = data.data;
22010     }
22011     Roo.data.MemoryProxy.superclass.constructor.call(this);
22012     this.data = data;
22013 };
22014
22015 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22016     /**
22017      * Load data from the requested source (in this case an in-memory
22018      * data object passed to the constructor), read the data object into
22019      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22020      * process that block using the passed callback.
22021      * @param {Object} params This parameter is not used by the MemoryProxy class.
22022      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22023      * object into a block of Roo.data.Records.
22024      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22025      * The function must be passed <ul>
22026      * <li>The Record block object</li>
22027      * <li>The "arg" argument from the load function</li>
22028      * <li>A boolean success indicator</li>
22029      * </ul>
22030      * @param {Object} scope The scope in which to call the callback
22031      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22032      */
22033     load : function(params, reader, callback, scope, arg){
22034         params = params || {};
22035         var result;
22036         try {
22037             result = reader.readRecords(this.data);
22038         }catch(e){
22039             this.fireEvent("loadexception", this, arg, null, e);
22040             callback.call(scope, null, arg, false);
22041             return;
22042         }
22043         callback.call(scope, result, arg, true);
22044     },
22045     
22046     // private
22047     update : function(params, records){
22048         
22049     }
22050 });/*
22051  * Based on:
22052  * Ext JS Library 1.1.1
22053  * Copyright(c) 2006-2007, Ext JS, LLC.
22054  *
22055  * Originally Released Under LGPL - original licence link has changed is not relivant.
22056  *
22057  * Fork - LGPL
22058  * <script type="text/javascript">
22059  */
22060 /**
22061  * @class Roo.data.HttpProxy
22062  * @extends Roo.data.DataProxy
22063  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22064  * configured to reference a certain URL.<br><br>
22065  * <p>
22066  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22067  * from which the running page was served.<br><br>
22068  * <p>
22069  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22070  * <p>
22071  * Be aware that to enable the browser to parse an XML document, the server must set
22072  * the Content-Type header in the HTTP response to "text/xml".
22073  * @constructor
22074  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22075  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22076  * will be used to make the request.
22077  */
22078 Roo.data.HttpProxy = function(conn){
22079     Roo.data.HttpProxy.superclass.constructor.call(this);
22080     // is conn a conn config or a real conn?
22081     this.conn = conn;
22082     this.useAjax = !conn || !conn.events;
22083   
22084 };
22085
22086 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22087     // thse are take from connection...
22088     
22089     /**
22090      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22091      */
22092     /**
22093      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22094      * extra parameters to each request made by this object. (defaults to undefined)
22095      */
22096     /**
22097      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22098      *  to each request made by this object. (defaults to undefined)
22099      */
22100     /**
22101      * @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)
22102      */
22103     /**
22104      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22105      */
22106      /**
22107      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22108      * @type Boolean
22109      */
22110   
22111
22112     /**
22113      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22114      * @type Boolean
22115      */
22116     /**
22117      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22118      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22119      * a finer-grained basis than the DataProxy events.
22120      */
22121     getConnection : function(){
22122         return this.useAjax ? Roo.Ajax : this.conn;
22123     },
22124
22125     /**
22126      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22127      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22128      * process that block using the passed callback.
22129      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22130      * for the request to the remote server.
22131      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22132      * object into a block of Roo.data.Records.
22133      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22134      * The function must be passed <ul>
22135      * <li>The Record block object</li>
22136      * <li>The "arg" argument from the load function</li>
22137      * <li>A boolean success indicator</li>
22138      * </ul>
22139      * @param {Object} scope The scope in which to call the callback
22140      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22141      */
22142     load : function(params, reader, callback, scope, arg){
22143         if(this.fireEvent("beforeload", this, params) !== false){
22144             var  o = {
22145                 params : params || {},
22146                 request: {
22147                     callback : callback,
22148                     scope : scope,
22149                     arg : arg
22150                 },
22151                 reader: reader,
22152                 callback : this.loadResponse,
22153                 scope: this
22154             };
22155             if(this.useAjax){
22156                 Roo.applyIf(o, this.conn);
22157                 if(this.activeRequest){
22158                     Roo.Ajax.abort(this.activeRequest);
22159                 }
22160                 this.activeRequest = Roo.Ajax.request(o);
22161             }else{
22162                 this.conn.request(o);
22163             }
22164         }else{
22165             callback.call(scope||this, null, arg, false);
22166         }
22167     },
22168
22169     // private
22170     loadResponse : function(o, success, response){
22171         delete this.activeRequest;
22172         if(!success){
22173             this.fireEvent("loadexception", this, o, response);
22174             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22175             return;
22176         }
22177         var result;
22178         try {
22179             result = o.reader.read(response);
22180         }catch(e){
22181             this.fireEvent("loadexception", this, o, response, e);
22182             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22183             return;
22184         }
22185         
22186         this.fireEvent("load", this, o, o.request.arg);
22187         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22188     },
22189
22190     // private
22191     update : function(dataSet){
22192
22193     },
22194
22195     // private
22196     updateResponse : function(dataSet){
22197
22198     }
22199 });/*
22200  * Based on:
22201  * Ext JS Library 1.1.1
22202  * Copyright(c) 2006-2007, Ext JS, LLC.
22203  *
22204  * Originally Released Under LGPL - original licence link has changed is not relivant.
22205  *
22206  * Fork - LGPL
22207  * <script type="text/javascript">
22208  */
22209
22210 /**
22211  * @class Roo.data.ScriptTagProxy
22212  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22213  * other than the originating domain of the running page.<br><br>
22214  * <p>
22215  * <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
22216  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22217  * <p>
22218  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22219  * source code that is used as the source inside a &lt;script> tag.<br><br>
22220  * <p>
22221  * In order for the browser to process the returned data, the server must wrap the data object
22222  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22223  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22224  * depending on whether the callback name was passed:
22225  * <p>
22226  * <pre><code>
22227 boolean scriptTag = false;
22228 String cb = request.getParameter("callback");
22229 if (cb != null) {
22230     scriptTag = true;
22231     response.setContentType("text/javascript");
22232 } else {
22233     response.setContentType("application/x-json");
22234 }
22235 Writer out = response.getWriter();
22236 if (scriptTag) {
22237     out.write(cb + "(");
22238 }
22239 out.print(dataBlock.toJsonString());
22240 if (scriptTag) {
22241     out.write(");");
22242 }
22243 </pre></code>
22244  *
22245  * @constructor
22246  * @param {Object} config A configuration object.
22247  */
22248 Roo.data.ScriptTagProxy = function(config){
22249     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22250     Roo.apply(this, config);
22251     this.head = document.getElementsByTagName("head")[0];
22252 };
22253
22254 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22255
22256 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22257     /**
22258      * @cfg {String} url The URL from which to request the data object.
22259      */
22260     /**
22261      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22262      */
22263     timeout : 30000,
22264     /**
22265      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22266      * the server the name of the callback function set up by the load call to process the returned data object.
22267      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22268      * javascript output which calls this named function passing the data object as its only parameter.
22269      */
22270     callbackParam : "callback",
22271     /**
22272      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22273      * name to the request.
22274      */
22275     nocache : true,
22276
22277     /**
22278      * Load data from the configured URL, read the data object into
22279      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22280      * process that block using the passed callback.
22281      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22282      * for the request to the remote server.
22283      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22284      * object into a block of Roo.data.Records.
22285      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22286      * The function must be passed <ul>
22287      * <li>The Record block object</li>
22288      * <li>The "arg" argument from the load function</li>
22289      * <li>A boolean success indicator</li>
22290      * </ul>
22291      * @param {Object} scope The scope in which to call the callback
22292      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22293      */
22294     load : function(params, reader, callback, scope, arg){
22295         if(this.fireEvent("beforeload", this, params) !== false){
22296
22297             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22298
22299             var url = this.url;
22300             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22301             if(this.nocache){
22302                 url += "&_dc=" + (new Date().getTime());
22303             }
22304             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22305             var trans = {
22306                 id : transId,
22307                 cb : "stcCallback"+transId,
22308                 scriptId : "stcScript"+transId,
22309                 params : params,
22310                 arg : arg,
22311                 url : url,
22312                 callback : callback,
22313                 scope : scope,
22314                 reader : reader
22315             };
22316             var conn = this;
22317
22318             window[trans.cb] = function(o){
22319                 conn.handleResponse(o, trans);
22320             };
22321
22322             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22323
22324             if(this.autoAbort !== false){
22325                 this.abort();
22326             }
22327
22328             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22329
22330             var script = document.createElement("script");
22331             script.setAttribute("src", url);
22332             script.setAttribute("type", "text/javascript");
22333             script.setAttribute("id", trans.scriptId);
22334             this.head.appendChild(script);
22335
22336             this.trans = trans;
22337         }else{
22338             callback.call(scope||this, null, arg, false);
22339         }
22340     },
22341
22342     // private
22343     isLoading : function(){
22344         return this.trans ? true : false;
22345     },
22346
22347     /**
22348      * Abort the current server request.
22349      */
22350     abort : function(){
22351         if(this.isLoading()){
22352             this.destroyTrans(this.trans);
22353         }
22354     },
22355
22356     // private
22357     destroyTrans : function(trans, isLoaded){
22358         this.head.removeChild(document.getElementById(trans.scriptId));
22359         clearTimeout(trans.timeoutId);
22360         if(isLoaded){
22361             window[trans.cb] = undefined;
22362             try{
22363                 delete window[trans.cb];
22364             }catch(e){}
22365         }else{
22366             // if hasn't been loaded, wait for load to remove it to prevent script error
22367             window[trans.cb] = function(){
22368                 window[trans.cb] = undefined;
22369                 try{
22370                     delete window[trans.cb];
22371                 }catch(e){}
22372             };
22373         }
22374     },
22375
22376     // private
22377     handleResponse : function(o, trans){
22378         this.trans = false;
22379         this.destroyTrans(trans, true);
22380         var result;
22381         try {
22382             result = trans.reader.readRecords(o);
22383         }catch(e){
22384             this.fireEvent("loadexception", this, o, trans.arg, e);
22385             trans.callback.call(trans.scope||window, null, trans.arg, false);
22386             return;
22387         }
22388         this.fireEvent("load", this, o, trans.arg);
22389         trans.callback.call(trans.scope||window, result, trans.arg, true);
22390     },
22391
22392     // private
22393     handleFailure : function(trans){
22394         this.trans = false;
22395         this.destroyTrans(trans, false);
22396         this.fireEvent("loadexception", this, null, trans.arg);
22397         trans.callback.call(trans.scope||window, null, trans.arg, false);
22398     }
22399 });/*
22400  * Based on:
22401  * Ext JS Library 1.1.1
22402  * Copyright(c) 2006-2007, Ext JS, LLC.
22403  *
22404  * Originally Released Under LGPL - original licence link has changed is not relivant.
22405  *
22406  * Fork - LGPL
22407  * <script type="text/javascript">
22408  */
22409
22410 /**
22411  * @class Roo.data.JsonReader
22412  * @extends Roo.data.DataReader
22413  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22414  * based on mappings in a provided Roo.data.Record constructor.
22415  * 
22416  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22417  * in the reply previously. 
22418  * 
22419  * <p>
22420  * Example code:
22421  * <pre><code>
22422 var RecordDef = Roo.data.Record.create([
22423     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22424     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22425 ]);
22426 var myReader = new Roo.data.JsonReader({
22427     totalProperty: "results",    // The property which contains the total dataset size (optional)
22428     root: "rows",                // The property which contains an Array of row objects
22429     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22430 }, RecordDef);
22431 </code></pre>
22432  * <p>
22433  * This would consume a JSON file like this:
22434  * <pre><code>
22435 { 'results': 2, 'rows': [
22436     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22437     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22438 }
22439 </code></pre>
22440  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22441  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22442  * paged from the remote server.
22443  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22444  * @cfg {String} root name of the property which contains the Array of row objects.
22445  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22446  * @constructor
22447  * Create a new JsonReader
22448  * @param {Object} meta Metadata configuration options
22449  * @param {Object} recordType Either an Array of field definition objects,
22450  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22451  */
22452 Roo.data.JsonReader = function(meta, recordType){
22453     
22454     meta = meta || {};
22455     // set some defaults:
22456     Roo.applyIf(meta, {
22457         totalProperty: 'total',
22458         successProperty : 'success',
22459         root : 'data',
22460         id : 'id'
22461     });
22462     
22463     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22464 };
22465 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22466     
22467     /**
22468      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22469      * Used by Store query builder to append _requestMeta to params.
22470      * 
22471      */
22472     metaFromRemote : false,
22473     /**
22474      * This method is only used by a DataProxy which has retrieved data from a remote server.
22475      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22476      * @return {Object} data A data block which is used by an Roo.data.Store object as
22477      * a cache of Roo.data.Records.
22478      */
22479     read : function(response){
22480         var json = response.responseText;
22481        
22482         var o = /* eval:var:o */ eval("("+json+")");
22483         if(!o) {
22484             throw {message: "JsonReader.read: Json object not found"};
22485         }
22486         
22487         if(o.metaData){
22488             
22489             delete this.ef;
22490             this.metaFromRemote = true;
22491             this.meta = o.metaData;
22492             this.recordType = Roo.data.Record.create(o.metaData.fields);
22493             this.onMetaChange(this.meta, this.recordType, o);
22494         }
22495         return this.readRecords(o);
22496     },
22497
22498     // private function a store will implement
22499     onMetaChange : function(meta, recordType, o){
22500
22501     },
22502
22503     /**
22504          * @ignore
22505          */
22506     simpleAccess: function(obj, subsc) {
22507         return obj[subsc];
22508     },
22509
22510         /**
22511          * @ignore
22512          */
22513     getJsonAccessor: function(){
22514         var re = /[\[\.]/;
22515         return function(expr) {
22516             try {
22517                 return(re.test(expr))
22518                     ? new Function("obj", "return obj." + expr)
22519                     : function(obj){
22520                         return obj[expr];
22521                     };
22522             } catch(e){}
22523             return Roo.emptyFn;
22524         };
22525     }(),
22526
22527     /**
22528      * Create a data block containing Roo.data.Records from an XML document.
22529      * @param {Object} o An object which contains an Array of row objects in the property specified
22530      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22531      * which contains the total size of the dataset.
22532      * @return {Object} data A data block which is used by an Roo.data.Store object as
22533      * a cache of Roo.data.Records.
22534      */
22535     readRecords : function(o){
22536         /**
22537          * After any data loads, the raw JSON data is available for further custom processing.
22538          * @type Object
22539          */
22540         this.o = o;
22541         var s = this.meta, Record = this.recordType,
22542             f = Record.prototype.fields, fi = f.items, fl = f.length;
22543
22544 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22545         if (!this.ef) {
22546             if(s.totalProperty) {
22547                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22548                 }
22549                 if(s.successProperty) {
22550                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22551                 }
22552                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22553                 if (s.id) {
22554                         var g = this.getJsonAccessor(s.id);
22555                         this.getId = function(rec) {
22556                                 var r = g(rec);
22557                                 return (r === undefined || r === "") ? null : r;
22558                         };
22559                 } else {
22560                         this.getId = function(){return null;};
22561                 }
22562             this.ef = [];
22563             for(var jj = 0; jj < fl; jj++){
22564                 f = fi[jj];
22565                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22566                 this.ef[jj] = this.getJsonAccessor(map);
22567             }
22568         }
22569
22570         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22571         if(s.totalProperty){
22572             var vt = parseInt(this.getTotal(o), 10);
22573             if(!isNaN(vt)){
22574                 totalRecords = vt;
22575             }
22576         }
22577         if(s.successProperty){
22578             var vs = this.getSuccess(o);
22579             if(vs === false || vs === 'false'){
22580                 success = false;
22581             }
22582         }
22583         var records = [];
22584             for(var i = 0; i < c; i++){
22585                     var n = root[i];
22586                 var values = {};
22587                 var id = this.getId(n);
22588                 for(var j = 0; j < fl; j++){
22589                     f = fi[j];
22590                 var v = this.ef[j](n);
22591                 if (!f.convert) {
22592                     Roo.log('missing convert for ' + f.name);
22593                     Roo.log(f);
22594                     continue;
22595                 }
22596                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22597                 }
22598                 var record = new Record(values, id);
22599                 record.json = n;
22600                 records[i] = record;
22601             }
22602             return {
22603             raw : o,
22604                 success : success,
22605                 records : records,
22606                 totalRecords : totalRecords
22607             };
22608     }
22609 });/*
22610  * Based on:
22611  * Ext JS Library 1.1.1
22612  * Copyright(c) 2006-2007, Ext JS, LLC.
22613  *
22614  * Originally Released Under LGPL - original licence link has changed is not relivant.
22615  *
22616  * Fork - LGPL
22617  * <script type="text/javascript">
22618  */
22619
22620 /**
22621  * @class Roo.data.XmlReader
22622  * @extends Roo.data.DataReader
22623  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22624  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22625  * <p>
22626  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22627  * header in the HTTP response must be set to "text/xml".</em>
22628  * <p>
22629  * Example code:
22630  * <pre><code>
22631 var RecordDef = Roo.data.Record.create([
22632    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22633    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22634 ]);
22635 var myReader = new Roo.data.XmlReader({
22636    totalRecords: "results", // The element which contains the total dataset size (optional)
22637    record: "row",           // The repeated element which contains row information
22638    id: "id"                 // The element within the row that provides an ID for the record (optional)
22639 }, RecordDef);
22640 </code></pre>
22641  * <p>
22642  * This would consume an XML file like this:
22643  * <pre><code>
22644 &lt;?xml?>
22645 &lt;dataset>
22646  &lt;results>2&lt;/results>
22647  &lt;row>
22648    &lt;id>1&lt;/id>
22649    &lt;name>Bill&lt;/name>
22650    &lt;occupation>Gardener&lt;/occupation>
22651  &lt;/row>
22652  &lt;row>
22653    &lt;id>2&lt;/id>
22654    &lt;name>Ben&lt;/name>
22655    &lt;occupation>Horticulturalist&lt;/occupation>
22656  &lt;/row>
22657 &lt;/dataset>
22658 </code></pre>
22659  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22660  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22661  * paged from the remote server.
22662  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22663  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22664  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22665  * a record identifier value.
22666  * @constructor
22667  * Create a new XmlReader
22668  * @param {Object} meta Metadata configuration options
22669  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22670  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22671  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22672  */
22673 Roo.data.XmlReader = function(meta, recordType){
22674     meta = meta || {};
22675     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22676 };
22677 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22678     /**
22679      * This method is only used by a DataProxy which has retrieved data from a remote server.
22680          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22681          * to contain a method called 'responseXML' that returns an XML document object.
22682      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22683      * a cache of Roo.data.Records.
22684      */
22685     read : function(response){
22686         var doc = response.responseXML;
22687         if(!doc) {
22688             throw {message: "XmlReader.read: XML Document not available"};
22689         }
22690         return this.readRecords(doc);
22691     },
22692
22693     /**
22694      * Create a data block containing Roo.data.Records from an XML document.
22695          * @param {Object} doc A parsed XML document.
22696      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22697      * a cache of Roo.data.Records.
22698      */
22699     readRecords : function(doc){
22700         /**
22701          * After any data loads/reads, the raw XML Document is available for further custom processing.
22702          * @type XMLDocument
22703          */
22704         this.xmlData = doc;
22705         var root = doc.documentElement || doc;
22706         var q = Roo.DomQuery;
22707         var recordType = this.recordType, fields = recordType.prototype.fields;
22708         var sid = this.meta.id;
22709         var totalRecords = 0, success = true;
22710         if(this.meta.totalRecords){
22711             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22712         }
22713         
22714         if(this.meta.success){
22715             var sv = q.selectValue(this.meta.success, root, true);
22716             success = sv !== false && sv !== 'false';
22717         }
22718         var records = [];
22719         var ns = q.select(this.meta.record, root);
22720         for(var i = 0, len = ns.length; i < len; i++) {
22721                 var n = ns[i];
22722                 var values = {};
22723                 var id = sid ? q.selectValue(sid, n) : undefined;
22724                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22725                     var f = fields.items[j];
22726                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22727                     v = f.convert(v);
22728                     values[f.name] = v;
22729                 }
22730                 var record = new recordType(values, id);
22731                 record.node = n;
22732                 records[records.length] = record;
22733             }
22734
22735             return {
22736                 success : success,
22737                 records : records,
22738                 totalRecords : totalRecords || records.length
22739             };
22740     }
22741 });/*
22742  * Based on:
22743  * Ext JS Library 1.1.1
22744  * Copyright(c) 2006-2007, Ext JS, LLC.
22745  *
22746  * Originally Released Under LGPL - original licence link has changed is not relivant.
22747  *
22748  * Fork - LGPL
22749  * <script type="text/javascript">
22750  */
22751
22752 /**
22753  * @class Roo.data.ArrayReader
22754  * @extends Roo.data.DataReader
22755  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22756  * Each element of that Array represents a row of data fields. The
22757  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22758  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22759  * <p>
22760  * Example code:.
22761  * <pre><code>
22762 var RecordDef = Roo.data.Record.create([
22763     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22764     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22765 ]);
22766 var myReader = new Roo.data.ArrayReader({
22767     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22768 }, RecordDef);
22769 </code></pre>
22770  * <p>
22771  * This would consume an Array like this:
22772  * <pre><code>
22773 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22774   </code></pre>
22775  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22776  * @constructor
22777  * Create a new JsonReader
22778  * @param {Object} meta Metadata configuration options.
22779  * @param {Object} recordType Either an Array of field definition objects
22780  * as specified to {@link Roo.data.Record#create},
22781  * or an {@link Roo.data.Record} object
22782  * created using {@link Roo.data.Record#create}.
22783  */
22784 Roo.data.ArrayReader = function(meta, recordType){
22785     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22786 };
22787
22788 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22789     /**
22790      * Create a data block containing Roo.data.Records from an XML document.
22791      * @param {Object} o An Array of row objects which represents the dataset.
22792      * @return {Object} data A data block which is used by an Roo.data.Store object as
22793      * a cache of Roo.data.Records.
22794      */
22795     readRecords : function(o){
22796         var sid = this.meta ? this.meta.id : null;
22797         var recordType = this.recordType, fields = recordType.prototype.fields;
22798         var records = [];
22799         var root = o;
22800             for(var i = 0; i < root.length; i++){
22801                     var n = root[i];
22802                 var values = {};
22803                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22804                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22805                 var f = fields.items[j];
22806                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22807                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22808                 v = f.convert(v);
22809                 values[f.name] = v;
22810             }
22811                 var record = new recordType(values, id);
22812                 record.json = n;
22813                 records[records.length] = record;
22814             }
22815             return {
22816                 records : records,
22817                 totalRecords : records.length
22818             };
22819     }
22820 });/*
22821  * Based on:
22822  * Ext JS Library 1.1.1
22823  * Copyright(c) 2006-2007, Ext JS, LLC.
22824  *
22825  * Originally Released Under LGPL - original licence link has changed is not relivant.
22826  *
22827  * Fork - LGPL
22828  * <script type="text/javascript">
22829  */
22830
22831
22832 /**
22833  * @class Roo.data.Tree
22834  * @extends Roo.util.Observable
22835  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22836  * in the tree have most standard DOM functionality.
22837  * @constructor
22838  * @param {Node} root (optional) The root node
22839  */
22840 Roo.data.Tree = function(root){
22841    this.nodeHash = {};
22842    /**
22843     * The root node for this tree
22844     * @type Node
22845     */
22846    this.root = null;
22847    if(root){
22848        this.setRootNode(root);
22849    }
22850    this.addEvents({
22851        /**
22852         * @event append
22853         * Fires when a new child node is appended to a node in this tree.
22854         * @param {Tree} tree The owner tree
22855         * @param {Node} parent The parent node
22856         * @param {Node} node The newly appended node
22857         * @param {Number} index The index of the newly appended node
22858         */
22859        "append" : true,
22860        /**
22861         * @event remove
22862         * Fires when a child node is removed from a node in this tree.
22863         * @param {Tree} tree The owner tree
22864         * @param {Node} parent The parent node
22865         * @param {Node} node The child node removed
22866         */
22867        "remove" : true,
22868        /**
22869         * @event move
22870         * Fires when a node is moved to a new location in the tree
22871         * @param {Tree} tree The owner tree
22872         * @param {Node} node The node moved
22873         * @param {Node} oldParent The old parent of this node
22874         * @param {Node} newParent The new parent of this node
22875         * @param {Number} index The index it was moved to
22876         */
22877        "move" : true,
22878        /**
22879         * @event insert
22880         * Fires when a new child node is inserted in a node in this tree.
22881         * @param {Tree} tree The owner tree
22882         * @param {Node} parent The parent node
22883         * @param {Node} node The child node inserted
22884         * @param {Node} refNode The child node the node was inserted before
22885         */
22886        "insert" : true,
22887        /**
22888         * @event beforeappend
22889         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22890         * @param {Tree} tree The owner tree
22891         * @param {Node} parent The parent node
22892         * @param {Node} node The child node to be appended
22893         */
22894        "beforeappend" : true,
22895        /**
22896         * @event beforeremove
22897         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22898         * @param {Tree} tree The owner tree
22899         * @param {Node} parent The parent node
22900         * @param {Node} node The child node to be removed
22901         */
22902        "beforeremove" : true,
22903        /**
22904         * @event beforemove
22905         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22906         * @param {Tree} tree The owner tree
22907         * @param {Node} node The node being moved
22908         * @param {Node} oldParent The parent of the node
22909         * @param {Node} newParent The new parent the node is moving to
22910         * @param {Number} index The index it is being moved to
22911         */
22912        "beforemove" : true,
22913        /**
22914         * @event beforeinsert
22915         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22916         * @param {Tree} tree The owner tree
22917         * @param {Node} parent The parent node
22918         * @param {Node} node The child node to be inserted
22919         * @param {Node} refNode The child node the node is being inserted before
22920         */
22921        "beforeinsert" : true
22922    });
22923
22924     Roo.data.Tree.superclass.constructor.call(this);
22925 };
22926
22927 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22928     pathSeparator: "/",
22929
22930     proxyNodeEvent : function(){
22931         return this.fireEvent.apply(this, arguments);
22932     },
22933
22934     /**
22935      * Returns the root node for this tree.
22936      * @return {Node}
22937      */
22938     getRootNode : function(){
22939         return this.root;
22940     },
22941
22942     /**
22943      * Sets the root node for this tree.
22944      * @param {Node} node
22945      * @return {Node}
22946      */
22947     setRootNode : function(node){
22948         this.root = node;
22949         node.ownerTree = this;
22950         node.isRoot = true;
22951         this.registerNode(node);
22952         return node;
22953     },
22954
22955     /**
22956      * Gets a node in this tree by its id.
22957      * @param {String} id
22958      * @return {Node}
22959      */
22960     getNodeById : function(id){
22961         return this.nodeHash[id];
22962     },
22963
22964     registerNode : function(node){
22965         this.nodeHash[node.id] = node;
22966     },
22967
22968     unregisterNode : function(node){
22969         delete this.nodeHash[node.id];
22970     },
22971
22972     toString : function(){
22973         return "[Tree"+(this.id?" "+this.id:"")+"]";
22974     }
22975 });
22976
22977 /**
22978  * @class Roo.data.Node
22979  * @extends Roo.util.Observable
22980  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
22981  * @cfg {String} id The id for this node. If one is not specified, one is generated.
22982  * @constructor
22983  * @param {Object} attributes The attributes/config for the node
22984  */
22985 Roo.data.Node = function(attributes){
22986     /**
22987      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
22988      * @type {Object}
22989      */
22990     this.attributes = attributes || {};
22991     this.leaf = this.attributes.leaf;
22992     /**
22993      * The node id. @type String
22994      */
22995     this.id = this.attributes.id;
22996     if(!this.id){
22997         this.id = Roo.id(null, "ynode-");
22998         this.attributes.id = this.id;
22999     }
23000      
23001     
23002     /**
23003      * All child nodes of this node. @type Array
23004      */
23005     this.childNodes = [];
23006     if(!this.childNodes.indexOf){ // indexOf is a must
23007         this.childNodes.indexOf = function(o){
23008             for(var i = 0, len = this.length; i < len; i++){
23009                 if(this[i] == o) {
23010                     return i;
23011                 }
23012             }
23013             return -1;
23014         };
23015     }
23016     /**
23017      * The parent node for this node. @type Node
23018      */
23019     this.parentNode = null;
23020     /**
23021      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23022      */
23023     this.firstChild = null;
23024     /**
23025      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23026      */
23027     this.lastChild = null;
23028     /**
23029      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23030      */
23031     this.previousSibling = null;
23032     /**
23033      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23034      */
23035     this.nextSibling = null;
23036
23037     this.addEvents({
23038        /**
23039         * @event append
23040         * Fires when a new child node is appended
23041         * @param {Tree} tree The owner tree
23042         * @param {Node} this This node
23043         * @param {Node} node The newly appended node
23044         * @param {Number} index The index of the newly appended node
23045         */
23046        "append" : true,
23047        /**
23048         * @event remove
23049         * Fires when a child node is removed
23050         * @param {Tree} tree The owner tree
23051         * @param {Node} this This node
23052         * @param {Node} node The removed node
23053         */
23054        "remove" : true,
23055        /**
23056         * @event move
23057         * Fires when this node is moved to a new location in the tree
23058         * @param {Tree} tree The owner tree
23059         * @param {Node} this This node
23060         * @param {Node} oldParent The old parent of this node
23061         * @param {Node} newParent The new parent of this node
23062         * @param {Number} index The index it was moved to
23063         */
23064        "move" : true,
23065        /**
23066         * @event insert
23067         * Fires when a new child node is inserted.
23068         * @param {Tree} tree The owner tree
23069         * @param {Node} this This node
23070         * @param {Node} node The child node inserted
23071         * @param {Node} refNode The child node the node was inserted before
23072         */
23073        "insert" : true,
23074        /**
23075         * @event beforeappend
23076         * Fires before a new child is appended, return false to cancel the append.
23077         * @param {Tree} tree The owner tree
23078         * @param {Node} this This node
23079         * @param {Node} node The child node to be appended
23080         */
23081        "beforeappend" : true,
23082        /**
23083         * @event beforeremove
23084         * Fires before a child is removed, return false to cancel the remove.
23085         * @param {Tree} tree The owner tree
23086         * @param {Node} this This node
23087         * @param {Node} node The child node to be removed
23088         */
23089        "beforeremove" : true,
23090        /**
23091         * @event beforemove
23092         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23093         * @param {Tree} tree The owner tree
23094         * @param {Node} this This node
23095         * @param {Node} oldParent The parent of this node
23096         * @param {Node} newParent The new parent this node is moving to
23097         * @param {Number} index The index it is being moved to
23098         */
23099        "beforemove" : true,
23100        /**
23101         * @event beforeinsert
23102         * Fires before a new child is inserted, return false to cancel the insert.
23103         * @param {Tree} tree The owner tree
23104         * @param {Node} this This node
23105         * @param {Node} node The child node to be inserted
23106         * @param {Node} refNode The child node the node is being inserted before
23107         */
23108        "beforeinsert" : true
23109    });
23110     this.listeners = this.attributes.listeners;
23111     Roo.data.Node.superclass.constructor.call(this);
23112 };
23113
23114 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23115     fireEvent : function(evtName){
23116         // first do standard event for this node
23117         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23118             return false;
23119         }
23120         // then bubble it up to the tree if the event wasn't cancelled
23121         var ot = this.getOwnerTree();
23122         if(ot){
23123             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23124                 return false;
23125             }
23126         }
23127         return true;
23128     },
23129
23130     /**
23131      * Returns true if this node is a leaf
23132      * @return {Boolean}
23133      */
23134     isLeaf : function(){
23135         return this.leaf === true;
23136     },
23137
23138     // private
23139     setFirstChild : function(node){
23140         this.firstChild = node;
23141     },
23142
23143     //private
23144     setLastChild : function(node){
23145         this.lastChild = node;
23146     },
23147
23148
23149     /**
23150      * Returns true if this node is the last child of its parent
23151      * @return {Boolean}
23152      */
23153     isLast : function(){
23154        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23155     },
23156
23157     /**
23158      * Returns true if this node is the first child of its parent
23159      * @return {Boolean}
23160      */
23161     isFirst : function(){
23162        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23163     },
23164
23165     hasChildNodes : function(){
23166         return !this.isLeaf() && this.childNodes.length > 0;
23167     },
23168
23169     /**
23170      * Insert node(s) as the last child node of this node.
23171      * @param {Node/Array} node The node or Array of nodes to append
23172      * @return {Node} The appended node if single append, or null if an array was passed
23173      */
23174     appendChild : function(node){
23175         var multi = false;
23176         if(node instanceof Array){
23177             multi = node;
23178         }else if(arguments.length > 1){
23179             multi = arguments;
23180         }
23181         // if passed an array or multiple args do them one by one
23182         if(multi){
23183             for(var i = 0, len = multi.length; i < len; i++) {
23184                 this.appendChild(multi[i]);
23185             }
23186         }else{
23187             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23188                 return false;
23189             }
23190             var index = this.childNodes.length;
23191             var oldParent = node.parentNode;
23192             // it's a move, make sure we move it cleanly
23193             if(oldParent){
23194                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23195                     return false;
23196                 }
23197                 oldParent.removeChild(node);
23198             }
23199             index = this.childNodes.length;
23200             if(index == 0){
23201                 this.setFirstChild(node);
23202             }
23203             this.childNodes.push(node);
23204             node.parentNode = this;
23205             var ps = this.childNodes[index-1];
23206             if(ps){
23207                 node.previousSibling = ps;
23208                 ps.nextSibling = node;
23209             }else{
23210                 node.previousSibling = null;
23211             }
23212             node.nextSibling = null;
23213             this.setLastChild(node);
23214             node.setOwnerTree(this.getOwnerTree());
23215             this.fireEvent("append", this.ownerTree, this, node, index);
23216             if(oldParent){
23217                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23218             }
23219             return node;
23220         }
23221     },
23222
23223     /**
23224      * Removes a child node from this node.
23225      * @param {Node} node The node to remove
23226      * @return {Node} The removed node
23227      */
23228     removeChild : function(node){
23229         var index = this.childNodes.indexOf(node);
23230         if(index == -1){
23231             return false;
23232         }
23233         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23234             return false;
23235         }
23236
23237         // remove it from childNodes collection
23238         this.childNodes.splice(index, 1);
23239
23240         // update siblings
23241         if(node.previousSibling){
23242             node.previousSibling.nextSibling = node.nextSibling;
23243         }
23244         if(node.nextSibling){
23245             node.nextSibling.previousSibling = node.previousSibling;
23246         }
23247
23248         // update child refs
23249         if(this.firstChild == node){
23250             this.setFirstChild(node.nextSibling);
23251         }
23252         if(this.lastChild == node){
23253             this.setLastChild(node.previousSibling);
23254         }
23255
23256         node.setOwnerTree(null);
23257         // clear any references from the node
23258         node.parentNode = null;
23259         node.previousSibling = null;
23260         node.nextSibling = null;
23261         this.fireEvent("remove", this.ownerTree, this, node);
23262         return node;
23263     },
23264
23265     /**
23266      * Inserts the first node before the second node in this nodes childNodes collection.
23267      * @param {Node} node The node to insert
23268      * @param {Node} refNode The node to insert before (if null the node is appended)
23269      * @return {Node} The inserted node
23270      */
23271     insertBefore : function(node, refNode){
23272         if(!refNode){ // like standard Dom, refNode can be null for append
23273             return this.appendChild(node);
23274         }
23275         // nothing to do
23276         if(node == refNode){
23277             return false;
23278         }
23279
23280         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23281             return false;
23282         }
23283         var index = this.childNodes.indexOf(refNode);
23284         var oldParent = node.parentNode;
23285         var refIndex = index;
23286
23287         // when moving internally, indexes will change after remove
23288         if(oldParent == this && this.childNodes.indexOf(node) < index){
23289             refIndex--;
23290         }
23291
23292         // it's a move, make sure we move it cleanly
23293         if(oldParent){
23294             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23295                 return false;
23296             }
23297             oldParent.removeChild(node);
23298         }
23299         if(refIndex == 0){
23300             this.setFirstChild(node);
23301         }
23302         this.childNodes.splice(refIndex, 0, node);
23303         node.parentNode = this;
23304         var ps = this.childNodes[refIndex-1];
23305         if(ps){
23306             node.previousSibling = ps;
23307             ps.nextSibling = node;
23308         }else{
23309             node.previousSibling = null;
23310         }
23311         node.nextSibling = refNode;
23312         refNode.previousSibling = node;
23313         node.setOwnerTree(this.getOwnerTree());
23314         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23315         if(oldParent){
23316             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23317         }
23318         return node;
23319     },
23320
23321     /**
23322      * Returns the child node at the specified index.
23323      * @param {Number} index
23324      * @return {Node}
23325      */
23326     item : function(index){
23327         return this.childNodes[index];
23328     },
23329
23330     /**
23331      * Replaces one child node in this node with another.
23332      * @param {Node} newChild The replacement node
23333      * @param {Node} oldChild The node to replace
23334      * @return {Node} The replaced node
23335      */
23336     replaceChild : function(newChild, oldChild){
23337         this.insertBefore(newChild, oldChild);
23338         this.removeChild(oldChild);
23339         return oldChild;
23340     },
23341
23342     /**
23343      * Returns the index of a child node
23344      * @param {Node} node
23345      * @return {Number} The index of the node or -1 if it was not found
23346      */
23347     indexOf : function(child){
23348         return this.childNodes.indexOf(child);
23349     },
23350
23351     /**
23352      * Returns the tree this node is in.
23353      * @return {Tree}
23354      */
23355     getOwnerTree : function(){
23356         // if it doesn't have one, look for one
23357         if(!this.ownerTree){
23358             var p = this;
23359             while(p){
23360                 if(p.ownerTree){
23361                     this.ownerTree = p.ownerTree;
23362                     break;
23363                 }
23364                 p = p.parentNode;
23365             }
23366         }
23367         return this.ownerTree;
23368     },
23369
23370     /**
23371      * Returns depth of this node (the root node has a depth of 0)
23372      * @return {Number}
23373      */
23374     getDepth : function(){
23375         var depth = 0;
23376         var p = this;
23377         while(p.parentNode){
23378             ++depth;
23379             p = p.parentNode;
23380         }
23381         return depth;
23382     },
23383
23384     // private
23385     setOwnerTree : function(tree){
23386         // if it's move, we need to update everyone
23387         if(tree != this.ownerTree){
23388             if(this.ownerTree){
23389                 this.ownerTree.unregisterNode(this);
23390             }
23391             this.ownerTree = tree;
23392             var cs = this.childNodes;
23393             for(var i = 0, len = cs.length; i < len; i++) {
23394                 cs[i].setOwnerTree(tree);
23395             }
23396             if(tree){
23397                 tree.registerNode(this);
23398             }
23399         }
23400     },
23401
23402     /**
23403      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23404      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23405      * @return {String} The path
23406      */
23407     getPath : function(attr){
23408         attr = attr || "id";
23409         var p = this.parentNode;
23410         var b = [this.attributes[attr]];
23411         while(p){
23412             b.unshift(p.attributes[attr]);
23413             p = p.parentNode;
23414         }
23415         var sep = this.getOwnerTree().pathSeparator;
23416         return sep + b.join(sep);
23417     },
23418
23419     /**
23420      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23421      * function call will be the scope provided or the current node. The arguments to the function
23422      * will be the args provided or the current node. If the function returns false at any point,
23423      * the bubble is stopped.
23424      * @param {Function} fn The function to call
23425      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23426      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23427      */
23428     bubble : function(fn, scope, args){
23429         var p = this;
23430         while(p){
23431             if(fn.call(scope || p, args || p) === false){
23432                 break;
23433             }
23434             p = p.parentNode;
23435         }
23436     },
23437
23438     /**
23439      * Cascades down 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 cascade is stopped on that branch.
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     cascade : function(fn, scope, args){
23448         if(fn.call(scope || this, args || this) !== false){
23449             var cs = this.childNodes;
23450             for(var i = 0, len = cs.length; i < len; i++) {
23451                 cs[i].cascade(fn, scope, args);
23452             }
23453         }
23454     },
23455
23456     /**
23457      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23458      * function call will be the scope provided or the current node. The arguments to the function
23459      * will be the args provided or the current node. If the function returns false at any point,
23460      * the iteration stops.
23461      * @param {Function} fn The function to call
23462      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23463      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23464      */
23465     eachChild : function(fn, scope, args){
23466         var cs = this.childNodes;
23467         for(var i = 0, len = cs.length; i < len; i++) {
23468                 if(fn.call(scope || this, args || cs[i]) === false){
23469                     break;
23470                 }
23471         }
23472     },
23473
23474     /**
23475      * Finds the first child that has the attribute with the specified value.
23476      * @param {String} attribute The attribute name
23477      * @param {Mixed} value The value to search for
23478      * @return {Node} The found child or null if none was found
23479      */
23480     findChild : function(attribute, value){
23481         var cs = this.childNodes;
23482         for(var i = 0, len = cs.length; i < len; i++) {
23483                 if(cs[i].attributes[attribute] == value){
23484                     return cs[i];
23485                 }
23486         }
23487         return null;
23488     },
23489
23490     /**
23491      * Finds the first child by a custom function. The child matches if the function passed
23492      * returns true.
23493      * @param {Function} fn
23494      * @param {Object} scope (optional)
23495      * @return {Node} The found child or null if none was found
23496      */
23497     findChildBy : function(fn, scope){
23498         var cs = this.childNodes;
23499         for(var i = 0, len = cs.length; i < len; i++) {
23500                 if(fn.call(scope||cs[i], cs[i]) === true){
23501                     return cs[i];
23502                 }
23503         }
23504         return null;
23505     },
23506
23507     /**
23508      * Sorts this nodes children using the supplied sort function
23509      * @param {Function} fn
23510      * @param {Object} scope (optional)
23511      */
23512     sort : function(fn, scope){
23513         var cs = this.childNodes;
23514         var len = cs.length;
23515         if(len > 0){
23516             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23517             cs.sort(sortFn);
23518             for(var i = 0; i < len; i++){
23519                 var n = cs[i];
23520                 n.previousSibling = cs[i-1];
23521                 n.nextSibling = cs[i+1];
23522                 if(i == 0){
23523                     this.setFirstChild(n);
23524                 }
23525                 if(i == len-1){
23526                     this.setLastChild(n);
23527                 }
23528             }
23529         }
23530     },
23531
23532     /**
23533      * Returns true if this node is an ancestor (at any point) of the passed node.
23534      * @param {Node} node
23535      * @return {Boolean}
23536      */
23537     contains : function(node){
23538         return node.isAncestor(this);
23539     },
23540
23541     /**
23542      * Returns true if the passed node is an ancestor (at any point) of this node.
23543      * @param {Node} node
23544      * @return {Boolean}
23545      */
23546     isAncestor : function(node){
23547         var p = this.parentNode;
23548         while(p){
23549             if(p == node){
23550                 return true;
23551             }
23552             p = p.parentNode;
23553         }
23554         return false;
23555     },
23556
23557     toString : function(){
23558         return "[Node"+(this.id?" "+this.id:"")+"]";
23559     }
23560 });/*
23561  * Based on:
23562  * Ext JS Library 1.1.1
23563  * Copyright(c) 2006-2007, Ext JS, LLC.
23564  *
23565  * Originally Released Under LGPL - original licence link has changed is not relivant.
23566  *
23567  * Fork - LGPL
23568  * <script type="text/javascript">
23569  */
23570  (function(){ 
23571 /**
23572  * @class Roo.Layer
23573  * @extends Roo.Element
23574  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23575  * automatic maintaining of shadow/shim positions.
23576  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23577  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23578  * you can pass a string with a CSS class name. False turns off the shadow.
23579  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23580  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23581  * @cfg {String} cls CSS class to add to the element
23582  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23583  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23584  * @constructor
23585  * @param {Object} config An object with config options.
23586  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23587  */
23588
23589 Roo.Layer = function(config, existingEl){
23590     config = config || {};
23591     var dh = Roo.DomHelper;
23592     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23593     if(existingEl){
23594         this.dom = Roo.getDom(existingEl);
23595     }
23596     if(!this.dom){
23597         var o = config.dh || {tag: "div", cls: "x-layer"};
23598         this.dom = dh.append(pel, o);
23599     }
23600     if(config.cls){
23601         this.addClass(config.cls);
23602     }
23603     this.constrain = config.constrain !== false;
23604     this.visibilityMode = Roo.Element.VISIBILITY;
23605     if(config.id){
23606         this.id = this.dom.id = config.id;
23607     }else{
23608         this.id = Roo.id(this.dom);
23609     }
23610     this.zindex = config.zindex || this.getZIndex();
23611     this.position("absolute", this.zindex);
23612     if(config.shadow){
23613         this.shadowOffset = config.shadowOffset || 4;
23614         this.shadow = new Roo.Shadow({
23615             offset : this.shadowOffset,
23616             mode : config.shadow
23617         });
23618     }else{
23619         this.shadowOffset = 0;
23620     }
23621     this.useShim = config.shim !== false && Roo.useShims;
23622     this.useDisplay = config.useDisplay;
23623     this.hide();
23624 };
23625
23626 var supr = Roo.Element.prototype;
23627
23628 // shims are shared among layer to keep from having 100 iframes
23629 var shims = [];
23630
23631 Roo.extend(Roo.Layer, Roo.Element, {
23632
23633     getZIndex : function(){
23634         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23635     },
23636
23637     getShim : function(){
23638         if(!this.useShim){
23639             return null;
23640         }
23641         if(this.shim){
23642             return this.shim;
23643         }
23644         var shim = shims.shift();
23645         if(!shim){
23646             shim = this.createShim();
23647             shim.enableDisplayMode('block');
23648             shim.dom.style.display = 'none';
23649             shim.dom.style.visibility = 'visible';
23650         }
23651         var pn = this.dom.parentNode;
23652         if(shim.dom.parentNode != pn){
23653             pn.insertBefore(shim.dom, this.dom);
23654         }
23655         shim.setStyle('z-index', this.getZIndex()-2);
23656         this.shim = shim;
23657         return shim;
23658     },
23659
23660     hideShim : function(){
23661         if(this.shim){
23662             this.shim.setDisplayed(false);
23663             shims.push(this.shim);
23664             delete this.shim;
23665         }
23666     },
23667
23668     disableShadow : function(){
23669         if(this.shadow){
23670             this.shadowDisabled = true;
23671             this.shadow.hide();
23672             this.lastShadowOffset = this.shadowOffset;
23673             this.shadowOffset = 0;
23674         }
23675     },
23676
23677     enableShadow : function(show){
23678         if(this.shadow){
23679             this.shadowDisabled = false;
23680             this.shadowOffset = this.lastShadowOffset;
23681             delete this.lastShadowOffset;
23682             if(show){
23683                 this.sync(true);
23684             }
23685         }
23686     },
23687
23688     // private
23689     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23690     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23691     sync : function(doShow){
23692         var sw = this.shadow;
23693         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23694             var sh = this.getShim();
23695
23696             var w = this.getWidth(),
23697                 h = this.getHeight();
23698
23699             var l = this.getLeft(true),
23700                 t = this.getTop(true);
23701
23702             if(sw && !this.shadowDisabled){
23703                 if(doShow && !sw.isVisible()){
23704                     sw.show(this);
23705                 }else{
23706                     sw.realign(l, t, w, h);
23707                 }
23708                 if(sh){
23709                     if(doShow){
23710                        sh.show();
23711                     }
23712                     // fit the shim behind the shadow, so it is shimmed too
23713                     var a = sw.adjusts, s = sh.dom.style;
23714                     s.left = (Math.min(l, l+a.l))+"px";
23715                     s.top = (Math.min(t, t+a.t))+"px";
23716                     s.width = (w+a.w)+"px";
23717                     s.height = (h+a.h)+"px";
23718                 }
23719             }else if(sh){
23720                 if(doShow){
23721                    sh.show();
23722                 }
23723                 sh.setSize(w, h);
23724                 sh.setLeftTop(l, t);
23725             }
23726             
23727         }
23728     },
23729
23730     // private
23731     destroy : function(){
23732         this.hideShim();
23733         if(this.shadow){
23734             this.shadow.hide();
23735         }
23736         this.removeAllListeners();
23737         var pn = this.dom.parentNode;
23738         if(pn){
23739             pn.removeChild(this.dom);
23740         }
23741         Roo.Element.uncache(this.id);
23742     },
23743
23744     remove : function(){
23745         this.destroy();
23746     },
23747
23748     // private
23749     beginUpdate : function(){
23750         this.updating = true;
23751     },
23752
23753     // private
23754     endUpdate : function(){
23755         this.updating = false;
23756         this.sync(true);
23757     },
23758
23759     // private
23760     hideUnders : function(negOffset){
23761         if(this.shadow){
23762             this.shadow.hide();
23763         }
23764         this.hideShim();
23765     },
23766
23767     // private
23768     constrainXY : function(){
23769         if(this.constrain){
23770             var vw = Roo.lib.Dom.getViewWidth(),
23771                 vh = Roo.lib.Dom.getViewHeight();
23772             var s = Roo.get(document).getScroll();
23773
23774             var xy = this.getXY();
23775             var x = xy[0], y = xy[1];   
23776             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23777             // only move it if it needs it
23778             var moved = false;
23779             // first validate right/bottom
23780             if((x + w) > vw+s.left){
23781                 x = vw - w - this.shadowOffset;
23782                 moved = true;
23783             }
23784             if((y + h) > vh+s.top){
23785                 y = vh - h - this.shadowOffset;
23786                 moved = true;
23787             }
23788             // then make sure top/left isn't negative
23789             if(x < s.left){
23790                 x = s.left;
23791                 moved = true;
23792             }
23793             if(y < s.top){
23794                 y = s.top;
23795                 moved = true;
23796             }
23797             if(moved){
23798                 if(this.avoidY){
23799                     var ay = this.avoidY;
23800                     if(y <= ay && (y+h) >= ay){
23801                         y = ay-h-5;   
23802                     }
23803                 }
23804                 xy = [x, y];
23805                 this.storeXY(xy);
23806                 supr.setXY.call(this, xy);
23807                 this.sync();
23808             }
23809         }
23810     },
23811
23812     isVisible : function(){
23813         return this.visible;    
23814     },
23815
23816     // private
23817     showAction : function(){
23818         this.visible = true; // track visibility to prevent getStyle calls
23819         if(this.useDisplay === true){
23820             this.setDisplayed("");
23821         }else if(this.lastXY){
23822             supr.setXY.call(this, this.lastXY);
23823         }else if(this.lastLT){
23824             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23825         }
23826     },
23827
23828     // private
23829     hideAction : function(){
23830         this.visible = false;
23831         if(this.useDisplay === true){
23832             this.setDisplayed(false);
23833         }else{
23834             this.setLeftTop(-10000,-10000);
23835         }
23836     },
23837
23838     // overridden Element method
23839     setVisible : function(v, a, d, c, e){
23840         if(v){
23841             this.showAction();
23842         }
23843         if(a && v){
23844             var cb = function(){
23845                 this.sync(true);
23846                 if(c){
23847                     c();
23848                 }
23849             }.createDelegate(this);
23850             supr.setVisible.call(this, true, true, d, cb, e);
23851         }else{
23852             if(!v){
23853                 this.hideUnders(true);
23854             }
23855             var cb = c;
23856             if(a){
23857                 cb = function(){
23858                     this.hideAction();
23859                     if(c){
23860                         c();
23861                     }
23862                 }.createDelegate(this);
23863             }
23864             supr.setVisible.call(this, v, a, d, cb, e);
23865             if(v){
23866                 this.sync(true);
23867             }else if(!a){
23868                 this.hideAction();
23869             }
23870         }
23871     },
23872
23873     storeXY : function(xy){
23874         delete this.lastLT;
23875         this.lastXY = xy;
23876     },
23877
23878     storeLeftTop : function(left, top){
23879         delete this.lastXY;
23880         this.lastLT = [left, top];
23881     },
23882
23883     // private
23884     beforeFx : function(){
23885         this.beforeAction();
23886         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23887     },
23888
23889     // private
23890     afterFx : function(){
23891         Roo.Layer.superclass.afterFx.apply(this, arguments);
23892         this.sync(this.isVisible());
23893     },
23894
23895     // private
23896     beforeAction : function(){
23897         if(!this.updating && this.shadow){
23898             this.shadow.hide();
23899         }
23900     },
23901
23902     // overridden Element method
23903     setLeft : function(left){
23904         this.storeLeftTop(left, this.getTop(true));
23905         supr.setLeft.apply(this, arguments);
23906         this.sync();
23907     },
23908
23909     setTop : function(top){
23910         this.storeLeftTop(this.getLeft(true), top);
23911         supr.setTop.apply(this, arguments);
23912         this.sync();
23913     },
23914
23915     setLeftTop : function(left, top){
23916         this.storeLeftTop(left, top);
23917         supr.setLeftTop.apply(this, arguments);
23918         this.sync();
23919     },
23920
23921     setXY : function(xy, a, d, c, e){
23922         this.fixDisplay();
23923         this.beforeAction();
23924         this.storeXY(xy);
23925         var cb = this.createCB(c);
23926         supr.setXY.call(this, xy, a, d, cb, e);
23927         if(!a){
23928             cb();
23929         }
23930     },
23931
23932     // private
23933     createCB : function(c){
23934         var el = this;
23935         return function(){
23936             el.constrainXY();
23937             el.sync(true);
23938             if(c){
23939                 c();
23940             }
23941         };
23942     },
23943
23944     // overridden Element method
23945     setX : function(x, a, d, c, e){
23946         this.setXY([x, this.getY()], a, d, c, e);
23947     },
23948
23949     // overridden Element method
23950     setY : function(y, a, d, c, e){
23951         this.setXY([this.getX(), y], a, d, c, e);
23952     },
23953
23954     // overridden Element method
23955     setSize : function(w, h, a, d, c, e){
23956         this.beforeAction();
23957         var cb = this.createCB(c);
23958         supr.setSize.call(this, w, h, a, d, cb, e);
23959         if(!a){
23960             cb();
23961         }
23962     },
23963
23964     // overridden Element method
23965     setWidth : function(w, a, d, c, e){
23966         this.beforeAction();
23967         var cb = this.createCB(c);
23968         supr.setWidth.call(this, w, a, d, cb, e);
23969         if(!a){
23970             cb();
23971         }
23972     },
23973
23974     // overridden Element method
23975     setHeight : function(h, a, d, c, e){
23976         this.beforeAction();
23977         var cb = this.createCB(c);
23978         supr.setHeight.call(this, h, a, d, cb, e);
23979         if(!a){
23980             cb();
23981         }
23982     },
23983
23984     // overridden Element method
23985     setBounds : function(x, y, w, h, a, d, c, e){
23986         this.beforeAction();
23987         var cb = this.createCB(c);
23988         if(!a){
23989             this.storeXY([x, y]);
23990             supr.setXY.call(this, [x, y]);
23991             supr.setSize.call(this, w, h, a, d, cb, e);
23992             cb();
23993         }else{
23994             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23995         }
23996         return this;
23997     },
23998     
23999     /**
24000      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24001      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24002      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24003      * @param {Number} zindex The new z-index to set
24004      * @return {this} The Layer
24005      */
24006     setZIndex : function(zindex){
24007         this.zindex = zindex;
24008         this.setStyle("z-index", zindex + 2);
24009         if(this.shadow){
24010             this.shadow.setZIndex(zindex + 1);
24011         }
24012         if(this.shim){
24013             this.shim.setStyle("z-index", zindex);
24014         }
24015     }
24016 });
24017 })();/*
24018  * Based on:
24019  * Ext JS Library 1.1.1
24020  * Copyright(c) 2006-2007, Ext JS, LLC.
24021  *
24022  * Originally Released Under LGPL - original licence link has changed is not relivant.
24023  *
24024  * Fork - LGPL
24025  * <script type="text/javascript">
24026  */
24027
24028
24029 /**
24030  * @class Roo.Shadow
24031  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24032  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24033  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24034  * @constructor
24035  * Create a new Shadow
24036  * @param {Object} config The config object
24037  */
24038 Roo.Shadow = function(config){
24039     Roo.apply(this, config);
24040     if(typeof this.mode != "string"){
24041         this.mode = this.defaultMode;
24042     }
24043     var o = this.offset, a = {h: 0};
24044     var rad = Math.floor(this.offset/2);
24045     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24046         case "drop":
24047             a.w = 0;
24048             a.l = a.t = o;
24049             a.t -= 1;
24050             if(Roo.isIE){
24051                 a.l -= this.offset + rad;
24052                 a.t -= this.offset + rad;
24053                 a.w -= rad;
24054                 a.h -= rad;
24055                 a.t += 1;
24056             }
24057         break;
24058         case "sides":
24059             a.w = (o*2);
24060             a.l = -o;
24061             a.t = o-1;
24062             if(Roo.isIE){
24063                 a.l -= (this.offset - rad);
24064                 a.t -= this.offset + rad;
24065                 a.l += 1;
24066                 a.w -= (this.offset - rad)*2;
24067                 a.w -= rad + 1;
24068                 a.h -= 1;
24069             }
24070         break;
24071         case "frame":
24072             a.w = a.h = (o*2);
24073             a.l = a.t = -o;
24074             a.t += 1;
24075             a.h -= 2;
24076             if(Roo.isIE){
24077                 a.l -= (this.offset - rad);
24078                 a.t -= (this.offset - rad);
24079                 a.l += 1;
24080                 a.w -= (this.offset + rad + 1);
24081                 a.h -= (this.offset + rad);
24082                 a.h += 1;
24083             }
24084         break;
24085     };
24086
24087     this.adjusts = a;
24088 };
24089
24090 Roo.Shadow.prototype = {
24091     /**
24092      * @cfg {String} mode
24093      * The shadow display mode.  Supports the following options:<br />
24094      * sides: Shadow displays on both sides and bottom only<br />
24095      * frame: Shadow displays equally on all four sides<br />
24096      * drop: Traditional bottom-right drop shadow (default)
24097      */
24098     /**
24099      * @cfg {String} offset
24100      * The number of pixels to offset the shadow from the element (defaults to 4)
24101      */
24102     offset: 4,
24103
24104     // private
24105     defaultMode: "drop",
24106
24107     /**
24108      * Displays the shadow under the target element
24109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24110      */
24111     show : function(target){
24112         target = Roo.get(target);
24113         if(!this.el){
24114             this.el = Roo.Shadow.Pool.pull();
24115             if(this.el.dom.nextSibling != target.dom){
24116                 this.el.insertBefore(target);
24117             }
24118         }
24119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24120         if(Roo.isIE){
24121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24122         }
24123         this.realign(
24124             target.getLeft(true),
24125             target.getTop(true),
24126             target.getWidth(),
24127             target.getHeight()
24128         );
24129         this.el.dom.style.display = "block";
24130     },
24131
24132     /**
24133      * Returns true if the shadow is visible, else false
24134      */
24135     isVisible : function(){
24136         return this.el ? true : false;  
24137     },
24138
24139     /**
24140      * Direct alignment when values are already available. Show must be called at least once before
24141      * calling this method to ensure it is initialized.
24142      * @param {Number} left The target element left position
24143      * @param {Number} top The target element top position
24144      * @param {Number} width The target element width
24145      * @param {Number} height The target element height
24146      */
24147     realign : function(l, t, w, h){
24148         if(!this.el){
24149             return;
24150         }
24151         var a = this.adjusts, d = this.el.dom, s = d.style;
24152         var iea = 0;
24153         s.left = (l+a.l)+"px";
24154         s.top = (t+a.t)+"px";
24155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24156  
24157         if(s.width != sws || s.height != shs){
24158             s.width = sws;
24159             s.height = shs;
24160             if(!Roo.isIE){
24161                 var cn = d.childNodes;
24162                 var sww = Math.max(0, (sw-12))+"px";
24163                 cn[0].childNodes[1].style.width = sww;
24164                 cn[1].childNodes[1].style.width = sww;
24165                 cn[2].childNodes[1].style.width = sww;
24166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24167             }
24168         }
24169     },
24170
24171     /**
24172      * Hides this shadow
24173      */
24174     hide : function(){
24175         if(this.el){
24176             this.el.dom.style.display = "none";
24177             Roo.Shadow.Pool.push(this.el);
24178             delete this.el;
24179         }
24180     },
24181
24182     /**
24183      * Adjust the z-index of this shadow
24184      * @param {Number} zindex The new z-index
24185      */
24186     setZIndex : function(z){
24187         this.zIndex = z;
24188         if(this.el){
24189             this.el.setStyle("z-index", z);
24190         }
24191     }
24192 };
24193
24194 // Private utility class that manages the internal Shadow cache
24195 Roo.Shadow.Pool = function(){
24196     var p = [];
24197     var markup = Roo.isIE ?
24198                  '<div class="x-ie-shadow"></div>' :
24199                  '<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>';
24200     return {
24201         pull : function(){
24202             var sh = p.shift();
24203             if(!sh){
24204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24205                 sh.autoBoxAdjust = false;
24206             }
24207             return sh;
24208         },
24209
24210         push : function(sh){
24211             p.push(sh);
24212         }
24213     };
24214 }();/*
24215  * Based on:
24216  * Ext JS Library 1.1.1
24217  * Copyright(c) 2006-2007, Ext JS, LLC.
24218  *
24219  * Originally Released Under LGPL - original licence link has changed is not relivant.
24220  *
24221  * Fork - LGPL
24222  * <script type="text/javascript">
24223  */
24224
24225
24226 /**
24227  * @class Roo.SplitBar
24228  * @extends Roo.util.Observable
24229  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24230  * <br><br>
24231  * Usage:
24232  * <pre><code>
24233 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24234                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24235 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24236 split.minSize = 100;
24237 split.maxSize = 600;
24238 split.animate = true;
24239 split.on('moved', splitterMoved);
24240 </code></pre>
24241  * @constructor
24242  * Create a new SplitBar
24243  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24244  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24245  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24246  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24247                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24248                         position of the SplitBar).
24249  */
24250 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24251     
24252     /** @private */
24253     this.el = Roo.get(dragElement, true);
24254     this.el.dom.unselectable = "on";
24255     /** @private */
24256     this.resizingEl = Roo.get(resizingElement, true);
24257
24258     /**
24259      * @private
24260      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24261      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24262      * @type Number
24263      */
24264     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24265     
24266     /**
24267      * The minimum size of the resizing element. (Defaults to 0)
24268      * @type Number
24269      */
24270     this.minSize = 0;
24271     
24272     /**
24273      * The maximum size of the resizing element. (Defaults to 2000)
24274      * @type Number
24275      */
24276     this.maxSize = 2000;
24277     
24278     /**
24279      * Whether to animate the transition to the new size
24280      * @type Boolean
24281      */
24282     this.animate = false;
24283     
24284     /**
24285      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24286      * @type Boolean
24287      */
24288     this.useShim = false;
24289     
24290     /** @private */
24291     this.shim = null;
24292     
24293     if(!existingProxy){
24294         /** @private */
24295         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24296     }else{
24297         this.proxy = Roo.get(existingProxy).dom;
24298     }
24299     /** @private */
24300     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24301     
24302     /** @private */
24303     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24304     
24305     /** @private */
24306     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24307     
24308     /** @private */
24309     this.dragSpecs = {};
24310     
24311     /**
24312      * @private The adapter to use to positon and resize elements
24313      */
24314     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24315     this.adapter.init(this);
24316     
24317     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24318         /** @private */
24319         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24320         this.el.addClass("x-splitbar-h");
24321     }else{
24322         /** @private */
24323         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24324         this.el.addClass("x-splitbar-v");
24325     }
24326     
24327     this.addEvents({
24328         /**
24329          * @event resize
24330          * Fires when the splitter is moved (alias for {@link #event-moved})
24331          * @param {Roo.SplitBar} this
24332          * @param {Number} newSize the new width or height
24333          */
24334         "resize" : true,
24335         /**
24336          * @event moved
24337          * Fires when the splitter is moved
24338          * @param {Roo.SplitBar} this
24339          * @param {Number} newSize the new width or height
24340          */
24341         "moved" : true,
24342         /**
24343          * @event beforeresize
24344          * Fires before the splitter is dragged
24345          * @param {Roo.SplitBar} this
24346          */
24347         "beforeresize" : true,
24348
24349         "beforeapply" : true
24350     });
24351
24352     Roo.util.Observable.call(this);
24353 };
24354
24355 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24356     onStartProxyDrag : function(x, y){
24357         this.fireEvent("beforeresize", this);
24358         if(!this.overlay){
24359             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24360             o.unselectable();
24361             o.enableDisplayMode("block");
24362             // all splitbars share the same overlay
24363             Roo.SplitBar.prototype.overlay = o;
24364         }
24365         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24366         this.overlay.show();
24367         Roo.get(this.proxy).setDisplayed("block");
24368         var size = this.adapter.getElementSize(this);
24369         this.activeMinSize = this.getMinimumSize();;
24370         this.activeMaxSize = this.getMaximumSize();;
24371         var c1 = size - this.activeMinSize;
24372         var c2 = Math.max(this.activeMaxSize - size, 0);
24373         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24374             this.dd.resetConstraints();
24375             this.dd.setXConstraint(
24376                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24377                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24378             );
24379             this.dd.setYConstraint(0, 0);
24380         }else{
24381             this.dd.resetConstraints();
24382             this.dd.setXConstraint(0, 0);
24383             this.dd.setYConstraint(
24384                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24385                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24386             );
24387          }
24388         this.dragSpecs.startSize = size;
24389         this.dragSpecs.startPoint = [x, y];
24390         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24391     },
24392     
24393     /** 
24394      * @private Called after the drag operation by the DDProxy
24395      */
24396     onEndProxyDrag : function(e){
24397         Roo.get(this.proxy).setDisplayed(false);
24398         var endPoint = Roo.lib.Event.getXY(e);
24399         if(this.overlay){
24400             this.overlay.hide();
24401         }
24402         var newSize;
24403         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24404             newSize = this.dragSpecs.startSize + 
24405                 (this.placement == Roo.SplitBar.LEFT ?
24406                     endPoint[0] - this.dragSpecs.startPoint[0] :
24407                     this.dragSpecs.startPoint[0] - endPoint[0]
24408                 );
24409         }else{
24410             newSize = this.dragSpecs.startSize + 
24411                 (this.placement == Roo.SplitBar.TOP ?
24412                     endPoint[1] - this.dragSpecs.startPoint[1] :
24413                     this.dragSpecs.startPoint[1] - endPoint[1]
24414                 );
24415         }
24416         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24417         if(newSize != this.dragSpecs.startSize){
24418             if(this.fireEvent('beforeapply', this, newSize) !== false){
24419                 this.adapter.setElementSize(this, newSize);
24420                 this.fireEvent("moved", this, newSize);
24421                 this.fireEvent("resize", this, newSize);
24422             }
24423         }
24424     },
24425     
24426     /**
24427      * Get the adapter this SplitBar uses
24428      * @return The adapter object
24429      */
24430     getAdapter : function(){
24431         return this.adapter;
24432     },
24433     
24434     /**
24435      * Set the adapter this SplitBar uses
24436      * @param {Object} adapter A SplitBar adapter object
24437      */
24438     setAdapter : function(adapter){
24439         this.adapter = adapter;
24440         this.adapter.init(this);
24441     },
24442     
24443     /**
24444      * Gets the minimum size for the resizing element
24445      * @return {Number} The minimum size
24446      */
24447     getMinimumSize : function(){
24448         return this.minSize;
24449     },
24450     
24451     /**
24452      * Sets the minimum size for the resizing element
24453      * @param {Number} minSize The minimum size
24454      */
24455     setMinimumSize : function(minSize){
24456         this.minSize = minSize;
24457     },
24458     
24459     /**
24460      * Gets the maximum size for the resizing element
24461      * @return {Number} The maximum size
24462      */
24463     getMaximumSize : function(){
24464         return this.maxSize;
24465     },
24466     
24467     /**
24468      * Sets the maximum size for the resizing element
24469      * @param {Number} maxSize The maximum size
24470      */
24471     setMaximumSize : function(maxSize){
24472         this.maxSize = maxSize;
24473     },
24474     
24475     /**
24476      * Sets the initialize size for the resizing element
24477      * @param {Number} size The initial size
24478      */
24479     setCurrentSize : function(size){
24480         var oldAnimate = this.animate;
24481         this.animate = false;
24482         this.adapter.setElementSize(this, size);
24483         this.animate = oldAnimate;
24484     },
24485     
24486     /**
24487      * Destroy this splitbar. 
24488      * @param {Boolean} removeEl True to remove the element
24489      */
24490     destroy : function(removeEl){
24491         if(this.shim){
24492             this.shim.remove();
24493         }
24494         this.dd.unreg();
24495         this.proxy.parentNode.removeChild(this.proxy);
24496         if(removeEl){
24497             this.el.remove();
24498         }
24499     }
24500 });
24501
24502 /**
24503  * @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.
24504  */
24505 Roo.SplitBar.createProxy = function(dir){
24506     var proxy = new Roo.Element(document.createElement("div"));
24507     proxy.unselectable();
24508     var cls = 'x-splitbar-proxy';
24509     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24510     document.body.appendChild(proxy.dom);
24511     return proxy.dom;
24512 };
24513
24514 /** 
24515  * @class Roo.SplitBar.BasicLayoutAdapter
24516  * Default Adapter. It assumes the splitter and resizing element are not positioned
24517  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24518  */
24519 Roo.SplitBar.BasicLayoutAdapter = function(){
24520 };
24521
24522 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24523     // do nothing for now
24524     init : function(s){
24525     
24526     },
24527     /**
24528      * Called before drag operations to get the current size of the resizing element. 
24529      * @param {Roo.SplitBar} s The SplitBar using this adapter
24530      */
24531      getElementSize : function(s){
24532         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24533             return s.resizingEl.getWidth();
24534         }else{
24535             return s.resizingEl.getHeight();
24536         }
24537     },
24538     
24539     /**
24540      * Called after drag operations to set the size of the resizing element.
24541      * @param {Roo.SplitBar} s The SplitBar using this adapter
24542      * @param {Number} newSize The new size to set
24543      * @param {Function} onComplete A function to be invoked when resizing is complete
24544      */
24545     setElementSize : function(s, newSize, onComplete){
24546         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24547             if(!s.animate){
24548                 s.resizingEl.setWidth(newSize);
24549                 if(onComplete){
24550                     onComplete(s, newSize);
24551                 }
24552             }else{
24553                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24554             }
24555         }else{
24556             
24557             if(!s.animate){
24558                 s.resizingEl.setHeight(newSize);
24559                 if(onComplete){
24560                     onComplete(s, newSize);
24561                 }
24562             }else{
24563                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24564             }
24565         }
24566     }
24567 };
24568
24569 /** 
24570  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24571  * @extends Roo.SplitBar.BasicLayoutAdapter
24572  * Adapter that  moves the splitter element to align with the resized sizing element. 
24573  * Used with an absolute positioned SplitBar.
24574  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24575  * document.body, make sure you assign an id to the body element.
24576  */
24577 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24578     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24579     this.container = Roo.get(container);
24580 };
24581
24582 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24583     init : function(s){
24584         this.basic.init(s);
24585     },
24586     
24587     getElementSize : function(s){
24588         return this.basic.getElementSize(s);
24589     },
24590     
24591     setElementSize : function(s, newSize, onComplete){
24592         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24593     },
24594     
24595     moveSplitter : function(s){
24596         var yes = Roo.SplitBar;
24597         switch(s.placement){
24598             case yes.LEFT:
24599                 s.el.setX(s.resizingEl.getRight());
24600                 break;
24601             case yes.RIGHT:
24602                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24603                 break;
24604             case yes.TOP:
24605                 s.el.setY(s.resizingEl.getBottom());
24606                 break;
24607             case yes.BOTTOM:
24608                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24609                 break;
24610         }
24611     }
24612 };
24613
24614 /**
24615  * Orientation constant - Create a vertical SplitBar
24616  * @static
24617  * @type Number
24618  */
24619 Roo.SplitBar.VERTICAL = 1;
24620
24621 /**
24622  * Orientation constant - Create a horizontal SplitBar
24623  * @static
24624  * @type Number
24625  */
24626 Roo.SplitBar.HORIZONTAL = 2;
24627
24628 /**
24629  * Placement constant - The resizing element is to the left of the splitter element
24630  * @static
24631  * @type Number
24632  */
24633 Roo.SplitBar.LEFT = 1;
24634
24635 /**
24636  * Placement constant - The resizing element is to the right of the splitter element
24637  * @static
24638  * @type Number
24639  */
24640 Roo.SplitBar.RIGHT = 2;
24641
24642 /**
24643  * Placement constant - The resizing element is positioned above the splitter element
24644  * @static
24645  * @type Number
24646  */
24647 Roo.SplitBar.TOP = 3;
24648
24649 /**
24650  * Placement constant - The resizing element is positioned under splitter element
24651  * @static
24652  * @type Number
24653  */
24654 Roo.SplitBar.BOTTOM = 4;
24655 /*
24656  * Based on:
24657  * Ext JS Library 1.1.1
24658  * Copyright(c) 2006-2007, Ext JS, LLC.
24659  *
24660  * Originally Released Under LGPL - original licence link has changed is not relivant.
24661  *
24662  * Fork - LGPL
24663  * <script type="text/javascript">
24664  */
24665
24666 /**
24667  * @class Roo.View
24668  * @extends Roo.util.Observable
24669  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24670  * This class also supports single and multi selection modes. <br>
24671  * Create a data model bound view:
24672  <pre><code>
24673  var store = new Roo.data.Store(...);
24674
24675  var view = new Roo.View({
24676     el : "my-element",
24677     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24678  
24679     singleSelect: true,
24680     selectedClass: "ydataview-selected",
24681     store: store
24682  });
24683
24684  // listen for node click?
24685  view.on("click", function(vw, index, node, e){
24686  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24687  });
24688
24689  // load XML data
24690  dataModel.load("foobar.xml");
24691  </code></pre>
24692  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24693  * <br><br>
24694  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24695  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24696  * 
24697  * Note: old style constructor is still suported (container, template, config)
24698  * 
24699  * @constructor
24700  * Create a new View
24701  * @param {Object} config The config object
24702  * 
24703  */
24704 Roo.View = function(config, depreciated_tpl, depreciated_config){
24705     
24706     if (typeof(depreciated_tpl) == 'undefined') {
24707         // new way.. - universal constructor.
24708         Roo.apply(this, config);
24709         this.el  = Roo.get(this.el);
24710     } else {
24711         // old format..
24712         this.el  = Roo.get(config);
24713         this.tpl = depreciated_tpl;
24714         Roo.apply(this, depreciated_config);
24715     }
24716     this.wrapEl  = this.el.wrap().wrap();
24717     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24718     
24719     
24720     if(typeof(this.tpl) == "string"){
24721         this.tpl = new Roo.Template(this.tpl);
24722     } else {
24723         // support xtype ctors..
24724         this.tpl = new Roo.factory(this.tpl, Roo);
24725     }
24726     
24727     
24728     this.tpl.compile();
24729    
24730   
24731     
24732      
24733     /** @private */
24734     this.addEvents({
24735         /**
24736          * @event beforeclick
24737          * Fires before a click is processed. Returns false to cancel the default action.
24738          * @param {Roo.View} this
24739          * @param {Number} index The index of the target node
24740          * @param {HTMLElement} node The target node
24741          * @param {Roo.EventObject} e The raw event object
24742          */
24743             "beforeclick" : true,
24744         /**
24745          * @event click
24746          * Fires when a template node is clicked.
24747          * @param {Roo.View} this
24748          * @param {Number} index The index of the target node
24749          * @param {HTMLElement} node The target node
24750          * @param {Roo.EventObject} e The raw event object
24751          */
24752             "click" : true,
24753         /**
24754          * @event dblclick
24755          * Fires when a template node is double clicked.
24756          * @param {Roo.View} this
24757          * @param {Number} index The index of the target node
24758          * @param {HTMLElement} node The target node
24759          * @param {Roo.EventObject} e The raw event object
24760          */
24761             "dblclick" : true,
24762         /**
24763          * @event contextmenu
24764          * Fires when a template node is right clicked.
24765          * @param {Roo.View} this
24766          * @param {Number} index The index of the target node
24767          * @param {HTMLElement} node The target node
24768          * @param {Roo.EventObject} e The raw event object
24769          */
24770             "contextmenu" : true,
24771         /**
24772          * @event selectionchange
24773          * Fires when the selected nodes change.
24774          * @param {Roo.View} this
24775          * @param {Array} selections Array of the selected nodes
24776          */
24777             "selectionchange" : true,
24778     
24779         /**
24780          * @event beforeselect
24781          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24782          * @param {Roo.View} this
24783          * @param {HTMLElement} node The node to be selected
24784          * @param {Array} selections Array of currently selected nodes
24785          */
24786             "beforeselect" : true,
24787         /**
24788          * @event preparedata
24789          * Fires on every row to render, to allow you to change the data.
24790          * @param {Roo.View} this
24791          * @param {Object} data to be rendered (change this)
24792          */
24793           "preparedata" : true
24794           
24795           
24796         });
24797
24798
24799
24800     this.el.on({
24801         "click": this.onClick,
24802         "dblclick": this.onDblClick,
24803         "contextmenu": this.onContextMenu,
24804         scope:this
24805     });
24806
24807     this.selections = [];
24808     this.nodes = [];
24809     this.cmp = new Roo.CompositeElementLite([]);
24810     if(this.store){
24811         this.store = Roo.factory(this.store, Roo.data);
24812         this.setStore(this.store, true);
24813     }
24814     
24815     if ( this.footer && this.footer.xtype) {
24816            
24817          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24818         
24819         this.footer.dataSource = this.store
24820         this.footer.container = fctr;
24821         this.footer = Roo.factory(this.footer, Roo);
24822         fctr.insertFirst(this.el);
24823         
24824         // this is a bit insane - as the paging toolbar seems to detach the el..
24825 //        dom.parentNode.parentNode.parentNode
24826          // they get detached?
24827     }
24828     
24829     
24830     Roo.View.superclass.constructor.call(this);
24831     
24832     
24833 };
24834
24835 Roo.extend(Roo.View, Roo.util.Observable, {
24836     
24837      /**
24838      * @cfg {Roo.data.Store} store Data store to load data from.
24839      */
24840     store : false,
24841     
24842     /**
24843      * @cfg {String|Roo.Element} el The container element.
24844      */
24845     el : '',
24846     
24847     /**
24848      * @cfg {String|Roo.Template} tpl The template used by this View 
24849      */
24850     tpl : false,
24851     /**
24852      * @cfg {String} dataName the named area of the template to use as the data area
24853      *                          Works with domtemplates roo-name="name"
24854      */
24855     dataName: false,
24856     /**
24857      * @cfg {String} selectedClass The css class to add to selected nodes
24858      */
24859     selectedClass : "x-view-selected",
24860      /**
24861      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24862      */
24863     emptyText : "",
24864     
24865     /**
24866      * @cfg {String} text to display on mask (default Loading)
24867      */
24868     mask : false,
24869     /**
24870      * @cfg {Boolean} multiSelect Allow multiple selection
24871      */
24872     multiSelect : false,
24873     /**
24874      * @cfg {Boolean} singleSelect Allow single selection
24875      */
24876     singleSelect:  false,
24877     
24878     /**
24879      * @cfg {Boolean} toggleSelect - selecting 
24880      */
24881     toggleSelect : false,
24882     
24883     /**
24884      * Returns the element this view is bound to.
24885      * @return {Roo.Element}
24886      */
24887     getEl : function(){
24888         return this.wrapEl;
24889     },
24890     
24891     
24892
24893     /**
24894      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24895      */
24896     refresh : function(){
24897         var t = this.tpl;
24898         
24899         // if we are using something like 'domtemplate', then
24900         // the what gets used is:
24901         // t.applySubtemplate(NAME, data, wrapping data..)
24902         // the outer template then get' applied with
24903         //     the store 'extra data'
24904         // and the body get's added to the
24905         //      roo-name="data" node?
24906         //      <span class='roo-tpl-{name}'></span> ?????
24907         
24908         
24909         
24910         this.clearSelections();
24911         this.el.update("");
24912         var html = [];
24913         var records = this.store.getRange();
24914         if(records.length < 1) {
24915             
24916             // is this valid??  = should it render a template??
24917             
24918             this.el.update(this.emptyText);
24919             return;
24920         }
24921         var el = this.el;
24922         if (this.dataName) {
24923             this.el.update(t.apply(this.store.meta)); //????
24924             el = this.el.child('.roo-tpl-' + this.dataName);
24925         }
24926         
24927         for(var i = 0, len = records.length; i < len; i++){
24928             var data = this.prepareData(records[i].data, i, records[i]);
24929             this.fireEvent("preparedata", this, data, i, records[i]);
24930             html[html.length] = Roo.util.Format.trim(
24931                 this.dataName ?
24932                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24933                     t.apply(data)
24934             );
24935         }
24936         
24937         
24938         
24939         el.update(html.join(""));
24940         this.nodes = el.dom.childNodes;
24941         this.updateIndexes(0);
24942     },
24943
24944     /**
24945      * Function to override to reformat the data that is sent to
24946      * the template for each node.
24947      * DEPRICATED - use the preparedata event handler.
24948      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24949      * a JSON object for an UpdateManager bound view).
24950      */
24951     prepareData : function(data, index, record)
24952     {
24953         this.fireEvent("preparedata", this, data, index, record);
24954         return data;
24955     },
24956
24957     onUpdate : function(ds, record){
24958         this.clearSelections();
24959         var index = this.store.indexOf(record);
24960         var n = this.nodes[index];
24961         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24962         n.parentNode.removeChild(n);
24963         this.updateIndexes(index, index);
24964     },
24965
24966     
24967     
24968 // --------- FIXME     
24969     onAdd : function(ds, records, index)
24970     {
24971         this.clearSelections();
24972         if(this.nodes.length == 0){
24973             this.refresh();
24974             return;
24975         }
24976         var n = this.nodes[index];
24977         for(var i = 0, len = records.length; i < len; i++){
24978             var d = this.prepareData(records[i].data, i, records[i]);
24979             if(n){
24980                 this.tpl.insertBefore(n, d);
24981             }else{
24982                 
24983                 this.tpl.append(this.el, d);
24984             }
24985         }
24986         this.updateIndexes(index);
24987     },
24988
24989     onRemove : function(ds, record, index){
24990         this.clearSelections();
24991         var el = this.dataName  ?
24992             this.el.child('.roo-tpl-' + this.dataName) :
24993             this.el; 
24994         el.dom.removeChild(this.nodes[index]);
24995         this.updateIndexes(index);
24996     },
24997
24998     /**
24999      * Refresh an individual node.
25000      * @param {Number} index
25001      */
25002     refreshNode : function(index){
25003         this.onUpdate(this.store, this.store.getAt(index));
25004     },
25005
25006     updateIndexes : function(startIndex, endIndex){
25007         var ns = this.nodes;
25008         startIndex = startIndex || 0;
25009         endIndex = endIndex || ns.length - 1;
25010         for(var i = startIndex; i <= endIndex; i++){
25011             ns[i].nodeIndex = i;
25012         }
25013     },
25014
25015     /**
25016      * Changes the data store this view uses and refresh the view.
25017      * @param {Store} store
25018      */
25019     setStore : function(store, initial){
25020         if(!initial && this.store){
25021             this.store.un("datachanged", this.refresh);
25022             this.store.un("add", this.onAdd);
25023             this.store.un("remove", this.onRemove);
25024             this.store.un("update", this.onUpdate);
25025             this.store.un("clear", this.refresh);
25026             this.store.un("beforeload", this.onBeforeLoad);
25027             this.store.un("load", this.onLoad);
25028             this.store.un("loadexception", this.onLoad);
25029         }
25030         if(store){
25031           
25032             store.on("datachanged", this.refresh, this);
25033             store.on("add", this.onAdd, this);
25034             store.on("remove", this.onRemove, this);
25035             store.on("update", this.onUpdate, this);
25036             store.on("clear", this.refresh, this);
25037             store.on("beforeload", this.onBeforeLoad, this);
25038             store.on("load", this.onLoad, this);
25039             store.on("loadexception", this.onLoad, this);
25040         }
25041         
25042         if(store){
25043             this.refresh();
25044         }
25045     },
25046     /**
25047      * onbeforeLoad - masks the loading area.
25048      *
25049      */
25050     onBeforeLoad : function()
25051     {
25052         this.el.update("");
25053         this.el.mask(this.mask ? this.mask : "Loading" ); 
25054     },
25055     onLoad : function ()
25056     {
25057         this.el.unmask();
25058     },
25059     
25060
25061     /**
25062      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25063      * @param {HTMLElement} node
25064      * @return {HTMLElement} The template node
25065      */
25066     findItemFromChild : function(node){
25067         var el = this.dataName  ?
25068             this.el.child('.roo-tpl-' + this.dataName,true) :
25069             this.el.dom; 
25070         
25071         if(!node || node.parentNode == el){
25072                     return node;
25073             }
25074             var p = node.parentNode;
25075             while(p && p != el){
25076             if(p.parentNode == el){
25077                 return p;
25078             }
25079             p = p.parentNode;
25080         }
25081             return null;
25082     },
25083
25084     /** @ignore */
25085     onClick : function(e){
25086         var item = this.findItemFromChild(e.getTarget());
25087         if(item){
25088             var index = this.indexOf(item);
25089             if(this.onItemClick(item, index, e) !== false){
25090                 this.fireEvent("click", this, index, item, e);
25091             }
25092         }else{
25093             this.clearSelections();
25094         }
25095     },
25096
25097     /** @ignore */
25098     onContextMenu : function(e){
25099         var item = this.findItemFromChild(e.getTarget());
25100         if(item){
25101             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25102         }
25103     },
25104
25105     /** @ignore */
25106     onDblClick : function(e){
25107         var item = this.findItemFromChild(e.getTarget());
25108         if(item){
25109             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25110         }
25111     },
25112
25113     onItemClick : function(item, index, e)
25114     {
25115         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25116             return false;
25117         }
25118         if (this.toggleSelect) {
25119             var m = this.isSelected(item) ? 'unselect' : 'select';
25120             Roo.log(m);
25121             var _t = this;
25122             _t[m](item, true, false);
25123             return true;
25124         }
25125         if(this.multiSelect || this.singleSelect){
25126             if(this.multiSelect && e.shiftKey && this.lastSelection){
25127                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25128             }else{
25129                 this.select(item, this.multiSelect && e.ctrlKey);
25130                 this.lastSelection = item;
25131             }
25132             e.preventDefault();
25133         }
25134         return true;
25135     },
25136
25137     /**
25138      * Get the number of selected nodes.
25139      * @return {Number}
25140      */
25141     getSelectionCount : function(){
25142         return this.selections.length;
25143     },
25144
25145     /**
25146      * Get the currently selected nodes.
25147      * @return {Array} An array of HTMLElements
25148      */
25149     getSelectedNodes : function(){
25150         return this.selections;
25151     },
25152
25153     /**
25154      * Get the indexes of the selected nodes.
25155      * @return {Array}
25156      */
25157     getSelectedIndexes : function(){
25158         var indexes = [], s = this.selections;
25159         for(var i = 0, len = s.length; i < len; i++){
25160             indexes.push(s[i].nodeIndex);
25161         }
25162         return indexes;
25163     },
25164
25165     /**
25166      * Clear all selections
25167      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25168      */
25169     clearSelections : function(suppressEvent){
25170         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25171             this.cmp.elements = this.selections;
25172             this.cmp.removeClass(this.selectedClass);
25173             this.selections = [];
25174             if(!suppressEvent){
25175                 this.fireEvent("selectionchange", this, this.selections);
25176             }
25177         }
25178     },
25179
25180     /**
25181      * Returns true if the passed node is selected
25182      * @param {HTMLElement/Number} node The node or node index
25183      * @return {Boolean}
25184      */
25185     isSelected : function(node){
25186         var s = this.selections;
25187         if(s.length < 1){
25188             return false;
25189         }
25190         node = this.getNode(node);
25191         return s.indexOf(node) !== -1;
25192     },
25193
25194     /**
25195      * Selects nodes.
25196      * @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
25197      * @param {Boolean} keepExisting (optional) true to keep existing selections
25198      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25199      */
25200     select : function(nodeInfo, keepExisting, suppressEvent){
25201         if(nodeInfo instanceof Array){
25202             if(!keepExisting){
25203                 this.clearSelections(true);
25204             }
25205             for(var i = 0, len = nodeInfo.length; i < len; i++){
25206                 this.select(nodeInfo[i], true, true);
25207             }
25208             return;
25209         } 
25210         var node = this.getNode(nodeInfo);
25211         if(!node || this.isSelected(node)){
25212             return; // already selected.
25213         }
25214         if(!keepExisting){
25215             this.clearSelections(true);
25216         }
25217         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25218             Roo.fly(node).addClass(this.selectedClass);
25219             this.selections.push(node);
25220             if(!suppressEvent){
25221                 this.fireEvent("selectionchange", this, this.selections);
25222             }
25223         }
25224         
25225         
25226     },
25227       /**
25228      * Unselects nodes.
25229      * @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
25230      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25231      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25232      */
25233     unselect : function(nodeInfo, keepExisting, suppressEvent)
25234     {
25235         if(nodeInfo instanceof Array){
25236             Roo.each(this.selections, function(s) {
25237                 this.unselect(s, nodeInfo);
25238             }, this);
25239             return;
25240         }
25241         var node = this.getNode(nodeInfo);
25242         if(!node || !this.isSelected(node)){
25243             Roo.log("not selected");
25244             return; // not selected.
25245         }
25246         // fireevent???
25247         var ns = [];
25248         Roo.each(this.selections, function(s) {
25249             if (s == node ) {
25250                 Roo.fly(node).removeClass(this.selectedClass);
25251
25252                 return;
25253             }
25254             ns.push(s);
25255         },this);
25256         
25257         this.selections= ns;
25258         this.fireEvent("selectionchange", this, this.selections);
25259     },
25260
25261     /**
25262      * Gets a template node.
25263      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25264      * @return {HTMLElement} The node or null if it wasn't found
25265      */
25266     getNode : function(nodeInfo){
25267         if(typeof nodeInfo == "string"){
25268             return document.getElementById(nodeInfo);
25269         }else if(typeof nodeInfo == "number"){
25270             return this.nodes[nodeInfo];
25271         }
25272         return nodeInfo;
25273     },
25274
25275     /**
25276      * Gets a range template nodes.
25277      * @param {Number} startIndex
25278      * @param {Number} endIndex
25279      * @return {Array} An array of nodes
25280      */
25281     getNodes : function(start, end){
25282         var ns = this.nodes;
25283         start = start || 0;
25284         end = typeof end == "undefined" ? ns.length - 1 : end;
25285         var nodes = [];
25286         if(start <= end){
25287             for(var i = start; i <= end; i++){
25288                 nodes.push(ns[i]);
25289             }
25290         } else{
25291             for(var i = start; i >= end; i--){
25292                 nodes.push(ns[i]);
25293             }
25294         }
25295         return nodes;
25296     },
25297
25298     /**
25299      * Finds the index of the passed node
25300      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25301      * @return {Number} The index of the node or -1
25302      */
25303     indexOf : function(node){
25304         node = this.getNode(node);
25305         if(typeof node.nodeIndex == "number"){
25306             return node.nodeIndex;
25307         }
25308         var ns = this.nodes;
25309         for(var i = 0, len = ns.length; i < len; i++){
25310             if(ns[i] == node){
25311                 return i;
25312             }
25313         }
25314         return -1;
25315     }
25316 });
25317 /*
25318  * Based on:
25319  * Ext JS Library 1.1.1
25320  * Copyright(c) 2006-2007, Ext JS, LLC.
25321  *
25322  * Originally Released Under LGPL - original licence link has changed is not relivant.
25323  *
25324  * Fork - LGPL
25325  * <script type="text/javascript">
25326  */
25327
25328 /**
25329  * @class Roo.JsonView
25330  * @extends Roo.View
25331  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25332 <pre><code>
25333 var view = new Roo.JsonView({
25334     container: "my-element",
25335     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25336     multiSelect: true, 
25337     jsonRoot: "data" 
25338 });
25339
25340 // listen for node click?
25341 view.on("click", function(vw, index, node, e){
25342     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25343 });
25344
25345 // direct load of JSON data
25346 view.load("foobar.php");
25347
25348 // Example from my blog list
25349 var tpl = new Roo.Template(
25350     '&lt;div class="entry"&gt;' +
25351     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25352     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25353     "&lt;/div&gt;&lt;hr /&gt;"
25354 );
25355
25356 var moreView = new Roo.JsonView({
25357     container :  "entry-list", 
25358     template : tpl,
25359     jsonRoot: "posts"
25360 });
25361 moreView.on("beforerender", this.sortEntries, this);
25362 moreView.load({
25363     url: "/blog/get-posts.php",
25364     params: "allposts=true",
25365     text: "Loading Blog Entries..."
25366 });
25367 </code></pre>
25368
25369 * Note: old code is supported with arguments : (container, template, config)
25370
25371
25372  * @constructor
25373  * Create a new JsonView
25374  * 
25375  * @param {Object} config The config object
25376  * 
25377  */
25378 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25379     
25380     
25381     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25382
25383     var um = this.el.getUpdateManager();
25384     um.setRenderer(this);
25385     um.on("update", this.onLoad, this);
25386     um.on("failure", this.onLoadException, this);
25387
25388     /**
25389      * @event beforerender
25390      * Fires before rendering of the downloaded JSON data.
25391      * @param {Roo.JsonView} this
25392      * @param {Object} data The JSON data loaded
25393      */
25394     /**
25395      * @event load
25396      * Fires when data is loaded.
25397      * @param {Roo.JsonView} this
25398      * @param {Object} data The JSON data loaded
25399      * @param {Object} response The raw Connect response object
25400      */
25401     /**
25402      * @event loadexception
25403      * Fires when loading fails.
25404      * @param {Roo.JsonView} this
25405      * @param {Object} response The raw Connect response object
25406      */
25407     this.addEvents({
25408         'beforerender' : true,
25409         'load' : true,
25410         'loadexception' : true
25411     });
25412 };
25413 Roo.extend(Roo.JsonView, Roo.View, {
25414     /**
25415      * @type {String} The root property in the loaded JSON object that contains the data
25416      */
25417     jsonRoot : "",
25418
25419     /**
25420      * Refreshes the view.
25421      */
25422     refresh : function(){
25423         this.clearSelections();
25424         this.el.update("");
25425         var html = [];
25426         var o = this.jsonData;
25427         if(o && o.length > 0){
25428             for(var i = 0, len = o.length; i < len; i++){
25429                 var data = this.prepareData(o[i], i, o);
25430                 html[html.length] = this.tpl.apply(data);
25431             }
25432         }else{
25433             html.push(this.emptyText);
25434         }
25435         this.el.update(html.join(""));
25436         this.nodes = this.el.dom.childNodes;
25437         this.updateIndexes(0);
25438     },
25439
25440     /**
25441      * 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.
25442      * @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:
25443      <pre><code>
25444      view.load({
25445          url: "your-url.php",
25446          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25447          callback: yourFunction,
25448          scope: yourObject, //(optional scope)
25449          discardUrl: false,
25450          nocache: false,
25451          text: "Loading...",
25452          timeout: 30,
25453          scripts: false
25454      });
25455      </code></pre>
25456      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25457      * 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.
25458      * @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}
25459      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25460      * @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.
25461      */
25462     load : function(){
25463         var um = this.el.getUpdateManager();
25464         um.update.apply(um, arguments);
25465     },
25466
25467     render : function(el, response){
25468         this.clearSelections();
25469         this.el.update("");
25470         var o;
25471         try{
25472             o = Roo.util.JSON.decode(response.responseText);
25473             if(this.jsonRoot){
25474                 
25475                 o = o[this.jsonRoot];
25476             }
25477         } catch(e){
25478         }
25479         /**
25480          * The current JSON data or null
25481          */
25482         this.jsonData = o;
25483         this.beforeRender();
25484         this.refresh();
25485     },
25486
25487 /**
25488  * Get the number of records in the current JSON dataset
25489  * @return {Number}
25490  */
25491     getCount : function(){
25492         return this.jsonData ? this.jsonData.length : 0;
25493     },
25494
25495 /**
25496  * Returns the JSON object for the specified node(s)
25497  * @param {HTMLElement/Array} node The node or an array of nodes
25498  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25499  * you get the JSON object for the node
25500  */
25501     getNodeData : function(node){
25502         if(node instanceof Array){
25503             var data = [];
25504             for(var i = 0, len = node.length; i < len; i++){
25505                 data.push(this.getNodeData(node[i]));
25506             }
25507             return data;
25508         }
25509         return this.jsonData[this.indexOf(node)] || null;
25510     },
25511
25512     beforeRender : function(){
25513         this.snapshot = this.jsonData;
25514         if(this.sortInfo){
25515             this.sort.apply(this, this.sortInfo);
25516         }
25517         this.fireEvent("beforerender", this, this.jsonData);
25518     },
25519
25520     onLoad : function(el, o){
25521         this.fireEvent("load", this, this.jsonData, o);
25522     },
25523
25524     onLoadException : function(el, o){
25525         this.fireEvent("loadexception", this, o);
25526     },
25527
25528 /**
25529  * Filter the data by a specific property.
25530  * @param {String} property A property on your JSON objects
25531  * @param {String/RegExp} value Either string that the property values
25532  * should start with, or a RegExp to test against the property
25533  */
25534     filter : function(property, value){
25535         if(this.jsonData){
25536             var data = [];
25537             var ss = this.snapshot;
25538             if(typeof value == "string"){
25539                 var vlen = value.length;
25540                 if(vlen == 0){
25541                     this.clearFilter();
25542                     return;
25543                 }
25544                 value = value.toLowerCase();
25545                 for(var i = 0, len = ss.length; i < len; i++){
25546                     var o = ss[i];
25547                     if(o[property].substr(0, vlen).toLowerCase() == value){
25548                         data.push(o);
25549                     }
25550                 }
25551             } else if(value.exec){ // regex?
25552                 for(var i = 0, len = ss.length; i < len; i++){
25553                     var o = ss[i];
25554                     if(value.test(o[property])){
25555                         data.push(o);
25556                     }
25557                 }
25558             } else{
25559                 return;
25560             }
25561             this.jsonData = data;
25562             this.refresh();
25563         }
25564     },
25565
25566 /**
25567  * Filter by a function. The passed function will be called with each
25568  * object in the current dataset. If the function returns true the value is kept,
25569  * otherwise it is filtered.
25570  * @param {Function} fn
25571  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25572  */
25573     filterBy : function(fn, scope){
25574         if(this.jsonData){
25575             var data = [];
25576             var ss = this.snapshot;
25577             for(var i = 0, len = ss.length; i < len; i++){
25578                 var o = ss[i];
25579                 if(fn.call(scope || this, o)){
25580                     data.push(o);
25581                 }
25582             }
25583             this.jsonData = data;
25584             this.refresh();
25585         }
25586     },
25587
25588 /**
25589  * Clears the current filter.
25590  */
25591     clearFilter : function(){
25592         if(this.snapshot && this.jsonData != this.snapshot){
25593             this.jsonData = this.snapshot;
25594             this.refresh();
25595         }
25596     },
25597
25598
25599 /**
25600  * Sorts the data for this view and refreshes it.
25601  * @param {String} property A property on your JSON objects to sort on
25602  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25603  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25604  */
25605     sort : function(property, dir, sortType){
25606         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25607         if(this.jsonData){
25608             var p = property;
25609             var dsc = dir && dir.toLowerCase() == "desc";
25610             var f = function(o1, o2){
25611                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25612                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25613                 ;
25614                 if(v1 < v2){
25615                     return dsc ? +1 : -1;
25616                 } else if(v1 > v2){
25617                     return dsc ? -1 : +1;
25618                 } else{
25619                     return 0;
25620                 }
25621             };
25622             this.jsonData.sort(f);
25623             this.refresh();
25624             if(this.jsonData != this.snapshot){
25625                 this.snapshot.sort(f);
25626             }
25627         }
25628     }
25629 });/*
25630  * Based on:
25631  * Ext JS Library 1.1.1
25632  * Copyright(c) 2006-2007, Ext JS, LLC.
25633  *
25634  * Originally Released Under LGPL - original licence link has changed is not relivant.
25635  *
25636  * Fork - LGPL
25637  * <script type="text/javascript">
25638  */
25639  
25640
25641 /**
25642  * @class Roo.ColorPalette
25643  * @extends Roo.Component
25644  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25645  * Here's an example of typical usage:
25646  * <pre><code>
25647 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25648 cp.render('my-div');
25649
25650 cp.on('select', function(palette, selColor){
25651     // do something with selColor
25652 });
25653 </code></pre>
25654  * @constructor
25655  * Create a new ColorPalette
25656  * @param {Object} config The config object
25657  */
25658 Roo.ColorPalette = function(config){
25659     Roo.ColorPalette.superclass.constructor.call(this, config);
25660     this.addEvents({
25661         /**
25662              * @event select
25663              * Fires when a color is selected
25664              * @param {ColorPalette} this
25665              * @param {String} color The 6-digit color hex code (without the # symbol)
25666              */
25667         select: true
25668     });
25669
25670     if(this.handler){
25671         this.on("select", this.handler, this.scope, true);
25672     }
25673 };
25674 Roo.extend(Roo.ColorPalette, Roo.Component, {
25675     /**
25676      * @cfg {String} itemCls
25677      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25678      */
25679     itemCls : "x-color-palette",
25680     /**
25681      * @cfg {String} value
25682      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25683      * the hex codes are case-sensitive.
25684      */
25685     value : null,
25686     clickEvent:'click',
25687     // private
25688     ctype: "Roo.ColorPalette",
25689
25690     /**
25691      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25692      */
25693     allowReselect : false,
25694
25695     /**
25696      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25697      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25698      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25699      * of colors with the width setting until the box is symmetrical.</p>
25700      * <p>You can override individual colors if needed:</p>
25701      * <pre><code>
25702 var cp = new Roo.ColorPalette();
25703 cp.colors[0] = "FF0000";  // change the first box to red
25704 </code></pre>
25705
25706 Or you can provide a custom array of your own for complete control:
25707 <pre><code>
25708 var cp = new Roo.ColorPalette();
25709 cp.colors = ["000000", "993300", "333300"];
25710 </code></pre>
25711      * @type Array
25712      */
25713     colors : [
25714         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25715         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25716         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25717         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25718         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25719     ],
25720
25721     // private
25722     onRender : function(container, position){
25723         var t = new Roo.MasterTemplate(
25724             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25725         );
25726         var c = this.colors;
25727         for(var i = 0, len = c.length; i < len; i++){
25728             t.add([c[i]]);
25729         }
25730         var el = document.createElement("div");
25731         el.className = this.itemCls;
25732         t.overwrite(el);
25733         container.dom.insertBefore(el, position);
25734         this.el = Roo.get(el);
25735         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25736         if(this.clickEvent != 'click'){
25737             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25738         }
25739     },
25740
25741     // private
25742     afterRender : function(){
25743         Roo.ColorPalette.superclass.afterRender.call(this);
25744         if(this.value){
25745             var s = this.value;
25746             this.value = null;
25747             this.select(s);
25748         }
25749     },
25750
25751     // private
25752     handleClick : function(e, t){
25753         e.preventDefault();
25754         if(!this.disabled){
25755             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25756             this.select(c.toUpperCase());
25757         }
25758     },
25759
25760     /**
25761      * Selects the specified color in the palette (fires the select event)
25762      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25763      */
25764     select : function(color){
25765         color = color.replace("#", "");
25766         if(color != this.value || this.allowReselect){
25767             var el = this.el;
25768             if(this.value){
25769                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25770             }
25771             el.child("a.color-"+color).addClass("x-color-palette-sel");
25772             this.value = color;
25773             this.fireEvent("select", this, color);
25774         }
25775     }
25776 });/*
25777  * Based on:
25778  * Ext JS Library 1.1.1
25779  * Copyright(c) 2006-2007, Ext JS, LLC.
25780  *
25781  * Originally Released Under LGPL - original licence link has changed is not relivant.
25782  *
25783  * Fork - LGPL
25784  * <script type="text/javascript">
25785  */
25786  
25787 /**
25788  * @class Roo.DatePicker
25789  * @extends Roo.Component
25790  * Simple date picker class.
25791  * @constructor
25792  * Create a new DatePicker
25793  * @param {Object} config The config object
25794  */
25795 Roo.DatePicker = function(config){
25796     Roo.DatePicker.superclass.constructor.call(this, config);
25797
25798     this.value = config && config.value ?
25799                  config.value.clearTime() : new Date().clearTime();
25800
25801     this.addEvents({
25802         /**
25803              * @event select
25804              * Fires when a date is selected
25805              * @param {DatePicker} this
25806              * @param {Date} date The selected date
25807              */
25808         'select': true,
25809         /**
25810              * @event monthchange
25811              * Fires when the displayed month changes 
25812              * @param {DatePicker} this
25813              * @param {Date} date The selected month
25814              */
25815         'monthchange': true
25816     });
25817
25818     if(this.handler){
25819         this.on("select", this.handler,  this.scope || this);
25820     }
25821     // build the disabledDatesRE
25822     if(!this.disabledDatesRE && this.disabledDates){
25823         var dd = this.disabledDates;
25824         var re = "(?:";
25825         for(var i = 0; i < dd.length; i++){
25826             re += dd[i];
25827             if(i != dd.length-1) re += "|";
25828         }
25829         this.disabledDatesRE = new RegExp(re + ")");
25830     }
25831 };
25832
25833 Roo.extend(Roo.DatePicker, Roo.Component, {
25834     /**
25835      * @cfg {String} todayText
25836      * The text to display on the button that selects the current date (defaults to "Today")
25837      */
25838     todayText : "Today",
25839     /**
25840      * @cfg {String} okText
25841      * The text to display on the ok button
25842      */
25843     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25844     /**
25845      * @cfg {String} cancelText
25846      * The text to display on the cancel button
25847      */
25848     cancelText : "Cancel",
25849     /**
25850      * @cfg {String} todayTip
25851      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25852      */
25853     todayTip : "{0} (Spacebar)",
25854     /**
25855      * @cfg {Date} minDate
25856      * Minimum allowable date (JavaScript date object, defaults to null)
25857      */
25858     minDate : null,
25859     /**
25860      * @cfg {Date} maxDate
25861      * Maximum allowable date (JavaScript date object, defaults to null)
25862      */
25863     maxDate : null,
25864     /**
25865      * @cfg {String} minText
25866      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25867      */
25868     minText : "This date is before the minimum date",
25869     /**
25870      * @cfg {String} maxText
25871      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25872      */
25873     maxText : "This date is after the maximum date",
25874     /**
25875      * @cfg {String} format
25876      * The default date format string which can be overriden for localization support.  The format must be
25877      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25878      */
25879     format : "m/d/y",
25880     /**
25881      * @cfg {Array} disabledDays
25882      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25883      */
25884     disabledDays : null,
25885     /**
25886      * @cfg {String} disabledDaysText
25887      * The tooltip to display when the date falls on a disabled day (defaults to "")
25888      */
25889     disabledDaysText : "",
25890     /**
25891      * @cfg {RegExp} disabledDatesRE
25892      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25893      */
25894     disabledDatesRE : null,
25895     /**
25896      * @cfg {String} disabledDatesText
25897      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25898      */
25899     disabledDatesText : "",
25900     /**
25901      * @cfg {Boolean} constrainToViewport
25902      * True to constrain the date picker to the viewport (defaults to true)
25903      */
25904     constrainToViewport : true,
25905     /**
25906      * @cfg {Array} monthNames
25907      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25908      */
25909     monthNames : Date.monthNames,
25910     /**
25911      * @cfg {Array} dayNames
25912      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25913      */
25914     dayNames : Date.dayNames,
25915     /**
25916      * @cfg {String} nextText
25917      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25918      */
25919     nextText: 'Next Month (Control+Right)',
25920     /**
25921      * @cfg {String} prevText
25922      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25923      */
25924     prevText: 'Previous Month (Control+Left)',
25925     /**
25926      * @cfg {String} monthYearText
25927      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25928      */
25929     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25930     /**
25931      * @cfg {Number} startDay
25932      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25933      */
25934     startDay : 0,
25935     /**
25936      * @cfg {Bool} showClear
25937      * Show a clear button (usefull for date form elements that can be blank.)
25938      */
25939     
25940     showClear: false,
25941     
25942     /**
25943      * Sets the value of the date field
25944      * @param {Date} value The date to set
25945      */
25946     setValue : function(value){
25947         var old = this.value;
25948         
25949         if (typeof(value) == 'string') {
25950          
25951             value = Date.parseDate(value, this.format);
25952         }
25953         if (!value) {
25954             value = new Date();
25955         }
25956         
25957         this.value = value.clearTime(true);
25958         if(this.el){
25959             this.update(this.value);
25960         }
25961     },
25962
25963     /**
25964      * Gets the current selected value of the date field
25965      * @return {Date} The selected date
25966      */
25967     getValue : function(){
25968         return this.value;
25969     },
25970
25971     // private
25972     focus : function(){
25973         if(this.el){
25974             this.update(this.activeDate);
25975         }
25976     },
25977
25978     // privateval
25979     onRender : function(container, position){
25980         
25981         var m = [
25982              '<table cellspacing="0">',
25983                 '<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>',
25984                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25985         var dn = this.dayNames;
25986         for(var i = 0; i < 7; i++){
25987             var d = this.startDay+i;
25988             if(d > 6){
25989                 d = d-7;
25990             }
25991             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25992         }
25993         m[m.length] = "</tr></thead><tbody><tr>";
25994         for(var i = 0; i < 42; i++) {
25995             if(i % 7 == 0 && i != 0){
25996                 m[m.length] = "</tr><tr>";
25997             }
25998             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25999         }
26000         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26001             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26002
26003         var el = document.createElement("div");
26004         el.className = "x-date-picker";
26005         el.innerHTML = m.join("");
26006
26007         container.dom.insertBefore(el, position);
26008
26009         this.el = Roo.get(el);
26010         this.eventEl = Roo.get(el.firstChild);
26011
26012         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26013             handler: this.showPrevMonth,
26014             scope: this,
26015             preventDefault:true,
26016             stopDefault:true
26017         });
26018
26019         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26020             handler: this.showNextMonth,
26021             scope: this,
26022             preventDefault:true,
26023             stopDefault:true
26024         });
26025
26026         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26027
26028         this.monthPicker = this.el.down('div.x-date-mp');
26029         this.monthPicker.enableDisplayMode('block');
26030         
26031         var kn = new Roo.KeyNav(this.eventEl, {
26032             "left" : function(e){
26033                 e.ctrlKey ?
26034                     this.showPrevMonth() :
26035                     this.update(this.activeDate.add("d", -1));
26036             },
26037
26038             "right" : function(e){
26039                 e.ctrlKey ?
26040                     this.showNextMonth() :
26041                     this.update(this.activeDate.add("d", 1));
26042             },
26043
26044             "up" : function(e){
26045                 e.ctrlKey ?
26046                     this.showNextYear() :
26047                     this.update(this.activeDate.add("d", -7));
26048             },
26049
26050             "down" : function(e){
26051                 e.ctrlKey ?
26052                     this.showPrevYear() :
26053                     this.update(this.activeDate.add("d", 7));
26054             },
26055
26056             "pageUp" : function(e){
26057                 this.showNextMonth();
26058             },
26059
26060             "pageDown" : function(e){
26061                 this.showPrevMonth();
26062             },
26063
26064             "enter" : function(e){
26065                 e.stopPropagation();
26066                 return true;
26067             },
26068
26069             scope : this
26070         });
26071
26072         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26073
26074         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26075
26076         this.el.unselectable();
26077         
26078         this.cells = this.el.select("table.x-date-inner tbody td");
26079         this.textNodes = this.el.query("table.x-date-inner tbody span");
26080
26081         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26082             text: "&#160;",
26083             tooltip: this.monthYearText
26084         });
26085
26086         this.mbtn.on('click', this.showMonthPicker, this);
26087         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26088
26089
26090         var today = (new Date()).dateFormat(this.format);
26091         
26092         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26093         if (this.showClear) {
26094             baseTb.add( new Roo.Toolbar.Fill());
26095         }
26096         baseTb.add({
26097             text: String.format(this.todayText, today),
26098             tooltip: String.format(this.todayTip, today),
26099             handler: this.selectToday,
26100             scope: this
26101         });
26102         
26103         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26104             
26105         //});
26106         if (this.showClear) {
26107             
26108             baseTb.add( new Roo.Toolbar.Fill());
26109             baseTb.add({
26110                 text: '&#160;',
26111                 cls: 'x-btn-icon x-btn-clear',
26112                 handler: function() {
26113                     //this.value = '';
26114                     this.fireEvent("select", this, '');
26115                 },
26116                 scope: this
26117             });
26118         }
26119         
26120         
26121         if(Roo.isIE){
26122             this.el.repaint();
26123         }
26124         this.update(this.value);
26125     },
26126
26127     createMonthPicker : function(){
26128         if(!this.monthPicker.dom.firstChild){
26129             var buf = ['<table border="0" cellspacing="0">'];
26130             for(var i = 0; i < 6; i++){
26131                 buf.push(
26132                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26133                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26134                     i == 0 ?
26135                     '<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>' :
26136                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26137                 );
26138             }
26139             buf.push(
26140                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26141                     this.okText,
26142                     '</button><button type="button" class="x-date-mp-cancel">',
26143                     this.cancelText,
26144                     '</button></td></tr>',
26145                 '</table>'
26146             );
26147             this.monthPicker.update(buf.join(''));
26148             this.monthPicker.on('click', this.onMonthClick, this);
26149             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26150
26151             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26152             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26153
26154             this.mpMonths.each(function(m, a, i){
26155                 i += 1;
26156                 if((i%2) == 0){
26157                     m.dom.xmonth = 5 + Math.round(i * .5);
26158                 }else{
26159                     m.dom.xmonth = Math.round((i-1) * .5);
26160                 }
26161             });
26162         }
26163     },
26164
26165     showMonthPicker : function(){
26166         this.createMonthPicker();
26167         var size = this.el.getSize();
26168         this.monthPicker.setSize(size);
26169         this.monthPicker.child('table').setSize(size);
26170
26171         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26172         this.updateMPMonth(this.mpSelMonth);
26173         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26174         this.updateMPYear(this.mpSelYear);
26175
26176         this.monthPicker.slideIn('t', {duration:.2});
26177     },
26178
26179     updateMPYear : function(y){
26180         this.mpyear = y;
26181         var ys = this.mpYears.elements;
26182         for(var i = 1; i <= 10; i++){
26183             var td = ys[i-1], y2;
26184             if((i%2) == 0){
26185                 y2 = y + Math.round(i * .5);
26186                 td.firstChild.innerHTML = y2;
26187                 td.xyear = y2;
26188             }else{
26189                 y2 = y - (5-Math.round(i * .5));
26190                 td.firstChild.innerHTML = y2;
26191                 td.xyear = y2;
26192             }
26193             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26194         }
26195     },
26196
26197     updateMPMonth : function(sm){
26198         this.mpMonths.each(function(m, a, i){
26199             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26200         });
26201     },
26202
26203     selectMPMonth: function(m){
26204         
26205     },
26206
26207     onMonthClick : function(e, t){
26208         e.stopEvent();
26209         var el = new Roo.Element(t), pn;
26210         if(el.is('button.x-date-mp-cancel')){
26211             this.hideMonthPicker();
26212         }
26213         else if(el.is('button.x-date-mp-ok')){
26214             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26215             this.hideMonthPicker();
26216         }
26217         else if(pn = el.up('td.x-date-mp-month', 2)){
26218             this.mpMonths.removeClass('x-date-mp-sel');
26219             pn.addClass('x-date-mp-sel');
26220             this.mpSelMonth = pn.dom.xmonth;
26221         }
26222         else if(pn = el.up('td.x-date-mp-year', 2)){
26223             this.mpYears.removeClass('x-date-mp-sel');
26224             pn.addClass('x-date-mp-sel');
26225             this.mpSelYear = pn.dom.xyear;
26226         }
26227         else if(el.is('a.x-date-mp-prev')){
26228             this.updateMPYear(this.mpyear-10);
26229         }
26230         else if(el.is('a.x-date-mp-next')){
26231             this.updateMPYear(this.mpyear+10);
26232         }
26233     },
26234
26235     onMonthDblClick : function(e, t){
26236         e.stopEvent();
26237         var el = new Roo.Element(t), pn;
26238         if(pn = el.up('td.x-date-mp-month', 2)){
26239             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26240             this.hideMonthPicker();
26241         }
26242         else if(pn = el.up('td.x-date-mp-year', 2)){
26243             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26244             this.hideMonthPicker();
26245         }
26246     },
26247
26248     hideMonthPicker : function(disableAnim){
26249         if(this.monthPicker){
26250             if(disableAnim === true){
26251                 this.monthPicker.hide();
26252             }else{
26253                 this.monthPicker.slideOut('t', {duration:.2});
26254             }
26255         }
26256     },
26257
26258     // private
26259     showPrevMonth : function(e){
26260         this.update(this.activeDate.add("mo", -1));
26261     },
26262
26263     // private
26264     showNextMonth : function(e){
26265         this.update(this.activeDate.add("mo", 1));
26266     },
26267
26268     // private
26269     showPrevYear : function(){
26270         this.update(this.activeDate.add("y", -1));
26271     },
26272
26273     // private
26274     showNextYear : function(){
26275         this.update(this.activeDate.add("y", 1));
26276     },
26277
26278     // private
26279     handleMouseWheel : function(e){
26280         var delta = e.getWheelDelta();
26281         if(delta > 0){
26282             this.showPrevMonth();
26283             e.stopEvent();
26284         } else if(delta < 0){
26285             this.showNextMonth();
26286             e.stopEvent();
26287         }
26288     },
26289
26290     // private
26291     handleDateClick : function(e, t){
26292         e.stopEvent();
26293         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26294             this.setValue(new Date(t.dateValue));
26295             this.fireEvent("select", this, this.value);
26296         }
26297     },
26298
26299     // private
26300     selectToday : function(){
26301         this.setValue(new Date().clearTime());
26302         this.fireEvent("select", this, this.value);
26303     },
26304
26305     // private
26306     update : function(date)
26307     {
26308         var vd = this.activeDate;
26309         this.activeDate = date;
26310         if(vd && this.el){
26311             var t = date.getTime();
26312             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26313                 this.cells.removeClass("x-date-selected");
26314                 this.cells.each(function(c){
26315                    if(c.dom.firstChild.dateValue == t){
26316                        c.addClass("x-date-selected");
26317                        setTimeout(function(){
26318                             try{c.dom.firstChild.focus();}catch(e){}
26319                        }, 50);
26320                        return false;
26321                    }
26322                 });
26323                 return;
26324             }
26325         }
26326         
26327         var days = date.getDaysInMonth();
26328         var firstOfMonth = date.getFirstDateOfMonth();
26329         var startingPos = firstOfMonth.getDay()-this.startDay;
26330
26331         if(startingPos <= this.startDay){
26332             startingPos += 7;
26333         }
26334
26335         var pm = date.add("mo", -1);
26336         var prevStart = pm.getDaysInMonth()-startingPos;
26337
26338         var cells = this.cells.elements;
26339         var textEls = this.textNodes;
26340         days += startingPos;
26341
26342         // convert everything to numbers so it's fast
26343         var day = 86400000;
26344         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26345         var today = new Date().clearTime().getTime();
26346         var sel = date.clearTime().getTime();
26347         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26348         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26349         var ddMatch = this.disabledDatesRE;
26350         var ddText = this.disabledDatesText;
26351         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26352         var ddaysText = this.disabledDaysText;
26353         var format = this.format;
26354
26355         var setCellClass = function(cal, cell){
26356             cell.title = "";
26357             var t = d.getTime();
26358             cell.firstChild.dateValue = t;
26359             if(t == today){
26360                 cell.className += " x-date-today";
26361                 cell.title = cal.todayText;
26362             }
26363             if(t == sel){
26364                 cell.className += " x-date-selected";
26365                 setTimeout(function(){
26366                     try{cell.firstChild.focus();}catch(e){}
26367                 }, 50);
26368             }
26369             // disabling
26370             if(t < min) {
26371                 cell.className = " x-date-disabled";
26372                 cell.title = cal.minText;
26373                 return;
26374             }
26375             if(t > max) {
26376                 cell.className = " x-date-disabled";
26377                 cell.title = cal.maxText;
26378                 return;
26379             }
26380             if(ddays){
26381                 if(ddays.indexOf(d.getDay()) != -1){
26382                     cell.title = ddaysText;
26383                     cell.className = " x-date-disabled";
26384                 }
26385             }
26386             if(ddMatch && format){
26387                 var fvalue = d.dateFormat(format);
26388                 if(ddMatch.test(fvalue)){
26389                     cell.title = ddText.replace("%0", fvalue);
26390                     cell.className = " x-date-disabled";
26391                 }
26392             }
26393         };
26394
26395         var i = 0;
26396         for(; i < startingPos; i++) {
26397             textEls[i].innerHTML = (++prevStart);
26398             d.setDate(d.getDate()+1);
26399             cells[i].className = "x-date-prevday";
26400             setCellClass(this, cells[i]);
26401         }
26402         for(; i < days; i++){
26403             intDay = i - startingPos + 1;
26404             textEls[i].innerHTML = (intDay);
26405             d.setDate(d.getDate()+1);
26406             cells[i].className = "x-date-active";
26407             setCellClass(this, cells[i]);
26408         }
26409         var extraDays = 0;
26410         for(; i < 42; i++) {
26411              textEls[i].innerHTML = (++extraDays);
26412              d.setDate(d.getDate()+1);
26413              cells[i].className = "x-date-nextday";
26414              setCellClass(this, cells[i]);
26415         }
26416
26417         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26418         this.fireEvent('monthchange', this, date);
26419         
26420         if(!this.internalRender){
26421             var main = this.el.dom.firstChild;
26422             var w = main.offsetWidth;
26423             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26424             Roo.fly(main).setWidth(w);
26425             this.internalRender = true;
26426             // opera does not respect the auto grow header center column
26427             // then, after it gets a width opera refuses to recalculate
26428             // without a second pass
26429             if(Roo.isOpera && !this.secondPass){
26430                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26431                 this.secondPass = true;
26432                 this.update.defer(10, this, [date]);
26433             }
26434         }
26435         
26436         
26437     }
26438 });        /*
26439  * Based on:
26440  * Ext JS Library 1.1.1
26441  * Copyright(c) 2006-2007, Ext JS, LLC.
26442  *
26443  * Originally Released Under LGPL - original licence link has changed is not relivant.
26444  *
26445  * Fork - LGPL
26446  * <script type="text/javascript">
26447  */
26448 /**
26449  * @class Roo.TabPanel
26450  * @extends Roo.util.Observable
26451  * A lightweight tab container.
26452  * <br><br>
26453  * Usage:
26454  * <pre><code>
26455 // basic tabs 1, built from existing content
26456 var tabs = new Roo.TabPanel("tabs1");
26457 tabs.addTab("script", "View Script");
26458 tabs.addTab("markup", "View Markup");
26459 tabs.activate("script");
26460
26461 // more advanced tabs, built from javascript
26462 var jtabs = new Roo.TabPanel("jtabs");
26463 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26464
26465 // set up the UpdateManager
26466 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26467 var updater = tab2.getUpdateManager();
26468 updater.setDefaultUrl("ajax1.htm");
26469 tab2.on('activate', updater.refresh, updater, true);
26470
26471 // Use setUrl for Ajax loading
26472 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26473 tab3.setUrl("ajax2.htm", null, true);
26474
26475 // Disabled tab
26476 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26477 tab4.disable();
26478
26479 jtabs.activate("jtabs-1");
26480  * </code></pre>
26481  * @constructor
26482  * Create a new TabPanel.
26483  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26484  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26485  */
26486 Roo.TabPanel = function(container, config){
26487     /**
26488     * The container element for this TabPanel.
26489     * @type Roo.Element
26490     */
26491     this.el = Roo.get(container, true);
26492     if(config){
26493         if(typeof config == "boolean"){
26494             this.tabPosition = config ? "bottom" : "top";
26495         }else{
26496             Roo.apply(this, config);
26497         }
26498     }
26499     if(this.tabPosition == "bottom"){
26500         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26501         this.el.addClass("x-tabs-bottom");
26502     }
26503     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26504     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26505     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26506     if(Roo.isIE){
26507         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26508     }
26509     if(this.tabPosition != "bottom"){
26510         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26511          * @type Roo.Element
26512          */
26513         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26514         this.el.addClass("x-tabs-top");
26515     }
26516     this.items = [];
26517
26518     this.bodyEl.setStyle("position", "relative");
26519
26520     this.active = null;
26521     this.activateDelegate = this.activate.createDelegate(this);
26522
26523     this.addEvents({
26524         /**
26525          * @event tabchange
26526          * Fires when the active tab changes
26527          * @param {Roo.TabPanel} this
26528          * @param {Roo.TabPanelItem} activePanel The new active tab
26529          */
26530         "tabchange": true,
26531         /**
26532          * @event beforetabchange
26533          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26534          * @param {Roo.TabPanel} this
26535          * @param {Object} e Set cancel to true on this object to cancel the tab change
26536          * @param {Roo.TabPanelItem} tab The tab being changed to
26537          */
26538         "beforetabchange" : true
26539     });
26540
26541     Roo.EventManager.onWindowResize(this.onResize, this);
26542     this.cpad = this.el.getPadding("lr");
26543     this.hiddenCount = 0;
26544
26545
26546     // toolbar on the tabbar support...
26547     if (this.toolbar) {
26548         var tcfg = this.toolbar;
26549         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26550         this.toolbar = new Roo.Toolbar(tcfg);
26551         if (Roo.isSafari) {
26552             var tbl = tcfg.container.child('table', true);
26553             tbl.setAttribute('width', '100%');
26554         }
26555         
26556     }
26557    
26558
26559
26560     Roo.TabPanel.superclass.constructor.call(this);
26561 };
26562
26563 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26564     /*
26565      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26566      */
26567     tabPosition : "top",
26568     /*
26569      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26570      */
26571     currentTabWidth : 0,
26572     /*
26573      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26574      */
26575     minTabWidth : 40,
26576     /*
26577      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26578      */
26579     maxTabWidth : 250,
26580     /*
26581      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26582      */
26583     preferredTabWidth : 175,
26584     /*
26585      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26586      */
26587     resizeTabs : false,
26588     /*
26589      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26590      */
26591     monitorResize : true,
26592     /*
26593      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26594      */
26595     toolbar : false,
26596
26597     /**
26598      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26599      * @param {String} id The id of the div to use <b>or create</b>
26600      * @param {String} text The text for the tab
26601      * @param {String} content (optional) Content to put in the TabPanelItem body
26602      * @param {Boolean} closable (optional) True to create a close icon on the tab
26603      * @return {Roo.TabPanelItem} The created TabPanelItem
26604      */
26605     addTab : function(id, text, content, closable){
26606         var item = new Roo.TabPanelItem(this, id, text, closable);
26607         this.addTabItem(item);
26608         if(content){
26609             item.setContent(content);
26610         }
26611         return item;
26612     },
26613
26614     /**
26615      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26616      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26617      * @return {Roo.TabPanelItem}
26618      */
26619     getTab : function(id){
26620         return this.items[id];
26621     },
26622
26623     /**
26624      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26625      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26626      */
26627     hideTab : function(id){
26628         var t = this.items[id];
26629         if(!t.isHidden()){
26630            t.setHidden(true);
26631            this.hiddenCount++;
26632            this.autoSizeTabs();
26633         }
26634     },
26635
26636     /**
26637      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26638      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26639      */
26640     unhideTab : function(id){
26641         var t = this.items[id];
26642         if(t.isHidden()){
26643            t.setHidden(false);
26644            this.hiddenCount--;
26645            this.autoSizeTabs();
26646         }
26647     },
26648
26649     /**
26650      * Adds an existing {@link Roo.TabPanelItem}.
26651      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26652      */
26653     addTabItem : function(item){
26654         this.items[item.id] = item;
26655         this.items.push(item);
26656         if(this.resizeTabs){
26657            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26658            this.autoSizeTabs();
26659         }else{
26660             item.autoSize();
26661         }
26662     },
26663
26664     /**
26665      * Removes a {@link Roo.TabPanelItem}.
26666      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26667      */
26668     removeTab : function(id){
26669         var items = this.items;
26670         var tab = items[id];
26671         if(!tab) { return; }
26672         var index = items.indexOf(tab);
26673         if(this.active == tab && items.length > 1){
26674             var newTab = this.getNextAvailable(index);
26675             if(newTab) {
26676                 newTab.activate();
26677             }
26678         }
26679         this.stripEl.dom.removeChild(tab.pnode.dom);
26680         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26681             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26682         }
26683         items.splice(index, 1);
26684         delete this.items[tab.id];
26685         tab.fireEvent("close", tab);
26686         tab.purgeListeners();
26687         this.autoSizeTabs();
26688     },
26689
26690     getNextAvailable : function(start){
26691         var items = this.items;
26692         var index = start;
26693         // look for a next tab that will slide over to
26694         // replace the one being removed
26695         while(index < items.length){
26696             var item = items[++index];
26697             if(item && !item.isHidden()){
26698                 return item;
26699             }
26700         }
26701         // if one isn't found select the previous tab (on the left)
26702         index = start;
26703         while(index >= 0){
26704             var item = items[--index];
26705             if(item && !item.isHidden()){
26706                 return item;
26707             }
26708         }
26709         return null;
26710     },
26711
26712     /**
26713      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26714      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26715      */
26716     disableTab : function(id){
26717         var tab = this.items[id];
26718         if(tab && this.active != tab){
26719             tab.disable();
26720         }
26721     },
26722
26723     /**
26724      * Enables a {@link Roo.TabPanelItem} that is disabled.
26725      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26726      */
26727     enableTab : function(id){
26728         var tab = this.items[id];
26729         tab.enable();
26730     },
26731
26732     /**
26733      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26734      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26735      * @return {Roo.TabPanelItem} The TabPanelItem.
26736      */
26737     activate : function(id){
26738         var tab = this.items[id];
26739         if(!tab){
26740             return null;
26741         }
26742         if(tab == this.active || tab.disabled){
26743             return tab;
26744         }
26745         var e = {};
26746         this.fireEvent("beforetabchange", this, e, tab);
26747         if(e.cancel !== true && !tab.disabled){
26748             if(this.active){
26749                 this.active.hide();
26750             }
26751             this.active = this.items[id];
26752             this.active.show();
26753             this.fireEvent("tabchange", this, this.active);
26754         }
26755         return tab;
26756     },
26757
26758     /**
26759      * Gets the active {@link Roo.TabPanelItem}.
26760      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26761      */
26762     getActiveTab : function(){
26763         return this.active;
26764     },
26765
26766     /**
26767      * Updates the tab body element to fit the height of the container element
26768      * for overflow scrolling
26769      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26770      */
26771     syncHeight : function(targetHeight){
26772         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26773         var bm = this.bodyEl.getMargins();
26774         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26775         this.bodyEl.setHeight(newHeight);
26776         return newHeight;
26777     },
26778
26779     onResize : function(){
26780         if(this.monitorResize){
26781             this.autoSizeTabs();
26782         }
26783     },
26784
26785     /**
26786      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26787      */
26788     beginUpdate : function(){
26789         this.updating = true;
26790     },
26791
26792     /**
26793      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26794      */
26795     endUpdate : function(){
26796         this.updating = false;
26797         this.autoSizeTabs();
26798     },
26799
26800     /**
26801      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26802      */
26803     autoSizeTabs : function(){
26804         var count = this.items.length;
26805         var vcount = count - this.hiddenCount;
26806         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26807         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26808         var availWidth = Math.floor(w / vcount);
26809         var b = this.stripBody;
26810         if(b.getWidth() > w){
26811             var tabs = this.items;
26812             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26813             if(availWidth < this.minTabWidth){
26814                 /*if(!this.sleft){    // incomplete scrolling code
26815                     this.createScrollButtons();
26816                 }
26817                 this.showScroll();
26818                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26819             }
26820         }else{
26821             if(this.currentTabWidth < this.preferredTabWidth){
26822                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26823             }
26824         }
26825     },
26826
26827     /**
26828      * Returns the number of tabs in this TabPanel.
26829      * @return {Number}
26830      */
26831      getCount : function(){
26832          return this.items.length;
26833      },
26834
26835     /**
26836      * Resizes all the tabs to the passed width
26837      * @param {Number} The new width
26838      */
26839     setTabWidth : function(width){
26840         this.currentTabWidth = width;
26841         for(var i = 0, len = this.items.length; i < len; i++) {
26842                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26843         }
26844     },
26845
26846     /**
26847      * Destroys this TabPanel
26848      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26849      */
26850     destroy : function(removeEl){
26851         Roo.EventManager.removeResizeListener(this.onResize, this);
26852         for(var i = 0, len = this.items.length; i < len; i++){
26853             this.items[i].purgeListeners();
26854         }
26855         if(removeEl === true){
26856             this.el.update("");
26857             this.el.remove();
26858         }
26859     }
26860 });
26861
26862 /**
26863  * @class Roo.TabPanelItem
26864  * @extends Roo.util.Observable
26865  * Represents an individual item (tab plus body) in a TabPanel.
26866  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26867  * @param {String} id The id of this TabPanelItem
26868  * @param {String} text The text for the tab of this TabPanelItem
26869  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26870  */
26871 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26872     /**
26873      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26874      * @type Roo.TabPanel
26875      */
26876     this.tabPanel = tabPanel;
26877     /**
26878      * The id for this TabPanelItem
26879      * @type String
26880      */
26881     this.id = id;
26882     /** @private */
26883     this.disabled = false;
26884     /** @private */
26885     this.text = text;
26886     /** @private */
26887     this.loaded = false;
26888     this.closable = closable;
26889
26890     /**
26891      * The body element for this TabPanelItem.
26892      * @type Roo.Element
26893      */
26894     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26895     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26896     this.bodyEl.setStyle("display", "block");
26897     this.bodyEl.setStyle("zoom", "1");
26898     this.hideAction();
26899
26900     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26901     /** @private */
26902     this.el = Roo.get(els.el, true);
26903     this.inner = Roo.get(els.inner, true);
26904     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26905     this.pnode = Roo.get(els.el.parentNode, true);
26906     this.el.on("mousedown", this.onTabMouseDown, this);
26907     this.el.on("click", this.onTabClick, this);
26908     /** @private */
26909     if(closable){
26910         var c = Roo.get(els.close, true);
26911         c.dom.title = this.closeText;
26912         c.addClassOnOver("close-over");
26913         c.on("click", this.closeClick, this);
26914      }
26915
26916     this.addEvents({
26917          /**
26918          * @event activate
26919          * Fires when this tab becomes the active tab.
26920          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26921          * @param {Roo.TabPanelItem} this
26922          */
26923         "activate": true,
26924         /**
26925          * @event beforeclose
26926          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26927          * @param {Roo.TabPanelItem} this
26928          * @param {Object} e Set cancel to true on this object to cancel the close.
26929          */
26930         "beforeclose": true,
26931         /**
26932          * @event close
26933          * Fires when this tab is closed.
26934          * @param {Roo.TabPanelItem} this
26935          */
26936          "close": true,
26937         /**
26938          * @event deactivate
26939          * Fires when this tab is no longer the active tab.
26940          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26941          * @param {Roo.TabPanelItem} this
26942          */
26943          "deactivate" : true
26944     });
26945     this.hidden = false;
26946
26947     Roo.TabPanelItem.superclass.constructor.call(this);
26948 };
26949
26950 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26951     purgeListeners : function(){
26952        Roo.util.Observable.prototype.purgeListeners.call(this);
26953        this.el.removeAllListeners();
26954     },
26955     /**
26956      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26957      */
26958     show : function(){
26959         this.pnode.addClass("on");
26960         this.showAction();
26961         if(Roo.isOpera){
26962             this.tabPanel.stripWrap.repaint();
26963         }
26964         this.fireEvent("activate", this.tabPanel, this);
26965     },
26966
26967     /**
26968      * Returns true if this tab is the active tab.
26969      * @return {Boolean}
26970      */
26971     isActive : function(){
26972         return this.tabPanel.getActiveTab() == this;
26973     },
26974
26975     /**
26976      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26977      */
26978     hide : function(){
26979         this.pnode.removeClass("on");
26980         this.hideAction();
26981         this.fireEvent("deactivate", this.tabPanel, this);
26982     },
26983
26984     hideAction : function(){
26985         this.bodyEl.hide();
26986         this.bodyEl.setStyle("position", "absolute");
26987         this.bodyEl.setLeft("-20000px");
26988         this.bodyEl.setTop("-20000px");
26989     },
26990
26991     showAction : function(){
26992         this.bodyEl.setStyle("position", "relative");
26993         this.bodyEl.setTop("");
26994         this.bodyEl.setLeft("");
26995         this.bodyEl.show();
26996     },
26997
26998     /**
26999      * Set the tooltip for the tab.
27000      * @param {String} tooltip The tab's tooltip
27001      */
27002     setTooltip : function(text){
27003         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27004             this.textEl.dom.qtip = text;
27005             this.textEl.dom.removeAttribute('title');
27006         }else{
27007             this.textEl.dom.title = text;
27008         }
27009     },
27010
27011     onTabClick : function(e){
27012         e.preventDefault();
27013         this.tabPanel.activate(this.id);
27014     },
27015
27016     onTabMouseDown : function(e){
27017         e.preventDefault();
27018         this.tabPanel.activate(this.id);
27019     },
27020
27021     getWidth : function(){
27022         return this.inner.getWidth();
27023     },
27024
27025     setWidth : function(width){
27026         var iwidth = width - this.pnode.getPadding("lr");
27027         this.inner.setWidth(iwidth);
27028         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27029         this.pnode.setWidth(width);
27030     },
27031
27032     /**
27033      * Show or hide the tab
27034      * @param {Boolean} hidden True to hide or false to show.
27035      */
27036     setHidden : function(hidden){
27037         this.hidden = hidden;
27038         this.pnode.setStyle("display", hidden ? "none" : "");
27039     },
27040
27041     /**
27042      * Returns true if this tab is "hidden"
27043      * @return {Boolean}
27044      */
27045     isHidden : function(){
27046         return this.hidden;
27047     },
27048
27049     /**
27050      * Returns the text for this tab
27051      * @return {String}
27052      */
27053     getText : function(){
27054         return this.text;
27055     },
27056
27057     autoSize : function(){
27058         //this.el.beginMeasure();
27059         this.textEl.setWidth(1);
27060         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27061         //this.el.endMeasure();
27062     },
27063
27064     /**
27065      * Sets the text for the tab (Note: this also sets the tooltip text)
27066      * @param {String} text The tab's text and tooltip
27067      */
27068     setText : function(text){
27069         this.text = text;
27070         this.textEl.update(text);
27071         this.setTooltip(text);
27072         if(!this.tabPanel.resizeTabs){
27073             this.autoSize();
27074         }
27075     },
27076     /**
27077      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27078      */
27079     activate : function(){
27080         this.tabPanel.activate(this.id);
27081     },
27082
27083     /**
27084      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27085      */
27086     disable : function(){
27087         if(this.tabPanel.active != this){
27088             this.disabled = true;
27089             this.pnode.addClass("disabled");
27090         }
27091     },
27092
27093     /**
27094      * Enables this TabPanelItem if it was previously disabled.
27095      */
27096     enable : function(){
27097         this.disabled = false;
27098         this.pnode.removeClass("disabled");
27099     },
27100
27101     /**
27102      * Sets the content for this TabPanelItem.
27103      * @param {String} content The content
27104      * @param {Boolean} loadScripts true to look for and load scripts
27105      */
27106     setContent : function(content, loadScripts){
27107         this.bodyEl.update(content, loadScripts);
27108     },
27109
27110     /**
27111      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27112      * @return {Roo.UpdateManager} The UpdateManager
27113      */
27114     getUpdateManager : function(){
27115         return this.bodyEl.getUpdateManager();
27116     },
27117
27118     /**
27119      * Set a URL to be used to load the content for this TabPanelItem.
27120      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27121      * @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)
27122      * @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)
27123      * @return {Roo.UpdateManager} The UpdateManager
27124      */
27125     setUrl : function(url, params, loadOnce){
27126         if(this.refreshDelegate){
27127             this.un('activate', this.refreshDelegate);
27128         }
27129         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27130         this.on("activate", this.refreshDelegate);
27131         return this.bodyEl.getUpdateManager();
27132     },
27133
27134     /** @private */
27135     _handleRefresh : function(url, params, loadOnce){
27136         if(!loadOnce || !this.loaded){
27137             var updater = this.bodyEl.getUpdateManager();
27138             updater.update(url, params, this._setLoaded.createDelegate(this));
27139         }
27140     },
27141
27142     /**
27143      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27144      *   Will fail silently if the setUrl method has not been called.
27145      *   This does not activate the panel, just updates its content.
27146      */
27147     refresh : function(){
27148         if(this.refreshDelegate){
27149            this.loaded = false;
27150            this.refreshDelegate();
27151         }
27152     },
27153
27154     /** @private */
27155     _setLoaded : function(){
27156         this.loaded = true;
27157     },
27158
27159     /** @private */
27160     closeClick : function(e){
27161         var o = {};
27162         e.stopEvent();
27163         this.fireEvent("beforeclose", this, o);
27164         if(o.cancel !== true){
27165             this.tabPanel.removeTab(this.id);
27166         }
27167     },
27168     /**
27169      * The text displayed in the tooltip for the close icon.
27170      * @type String
27171      */
27172     closeText : "Close this tab"
27173 });
27174
27175 /** @private */
27176 Roo.TabPanel.prototype.createStrip = function(container){
27177     var strip = document.createElement("div");
27178     strip.className = "x-tabs-wrap";
27179     container.appendChild(strip);
27180     return strip;
27181 };
27182 /** @private */
27183 Roo.TabPanel.prototype.createStripList = function(strip){
27184     // div wrapper for retard IE
27185     // returns the "tr" element.
27186     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27187         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27188         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27189     return strip.firstChild.firstChild.firstChild.firstChild;
27190 };
27191 /** @private */
27192 Roo.TabPanel.prototype.createBody = function(container){
27193     var body = document.createElement("div");
27194     Roo.id(body, "tab-body");
27195     Roo.fly(body).addClass("x-tabs-body");
27196     container.appendChild(body);
27197     return body;
27198 };
27199 /** @private */
27200 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27201     var body = Roo.getDom(id);
27202     if(!body){
27203         body = document.createElement("div");
27204         body.id = id;
27205     }
27206     Roo.fly(body).addClass("x-tabs-item-body");
27207     bodyEl.insertBefore(body, bodyEl.firstChild);
27208     return body;
27209 };
27210 /** @private */
27211 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27212     var td = document.createElement("td");
27213     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27214     //stripEl.appendChild(td);
27215     if(closable){
27216         td.className = "x-tabs-closable";
27217         if(!this.closeTpl){
27218             this.closeTpl = new Roo.Template(
27219                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27220                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27221                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27222             );
27223         }
27224         var el = this.closeTpl.overwrite(td, {"text": text});
27225         var close = el.getElementsByTagName("div")[0];
27226         var inner = el.getElementsByTagName("em")[0];
27227         return {"el": el, "close": close, "inner": inner};
27228     } else {
27229         if(!this.tabTpl){
27230             this.tabTpl = new Roo.Template(
27231                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27232                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27233             );
27234         }
27235         var el = this.tabTpl.overwrite(td, {"text": text});
27236         var inner = el.getElementsByTagName("em")[0];
27237         return {"el": el, "inner": inner};
27238     }
27239 };/*
27240  * Based on:
27241  * Ext JS Library 1.1.1
27242  * Copyright(c) 2006-2007, Ext JS, LLC.
27243  *
27244  * Originally Released Under LGPL - original licence link has changed is not relivant.
27245  *
27246  * Fork - LGPL
27247  * <script type="text/javascript">
27248  */
27249
27250 /**
27251  * @class Roo.Button
27252  * @extends Roo.util.Observable
27253  * Simple Button class
27254  * @cfg {String} text The button text
27255  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27256  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27257  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27258  * @cfg {Object} scope The scope of the handler
27259  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27260  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27261  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27262  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27263  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27264  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27265    applies if enableToggle = true)
27266  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27267  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27268   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27269  * @constructor
27270  * Create a new button
27271  * @param {Object} config The config object
27272  */
27273 Roo.Button = function(renderTo, config)
27274 {
27275     if (!config) {
27276         config = renderTo;
27277         renderTo = config.renderTo || false;
27278     }
27279     
27280     Roo.apply(this, config);
27281     this.addEvents({
27282         /**
27283              * @event click
27284              * Fires when this button is clicked
27285              * @param {Button} this
27286              * @param {EventObject} e The click event
27287              */
27288             "click" : true,
27289         /**
27290              * @event toggle
27291              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27292              * @param {Button} this
27293              * @param {Boolean} pressed
27294              */
27295             "toggle" : true,
27296         /**
27297              * @event mouseover
27298              * Fires when the mouse hovers over the button
27299              * @param {Button} this
27300              * @param {Event} e The event object
27301              */
27302         'mouseover' : true,
27303         /**
27304              * @event mouseout
27305              * Fires when the mouse exits the button
27306              * @param {Button} this
27307              * @param {Event} e The event object
27308              */
27309         'mouseout': true,
27310          /**
27311              * @event render
27312              * Fires when the button is rendered
27313              * @param {Button} this
27314              */
27315         'render': true
27316     });
27317     if(this.menu){
27318         this.menu = Roo.menu.MenuMgr.get(this.menu);
27319     }
27320     // register listeners first!!  - so render can be captured..
27321     Roo.util.Observable.call(this);
27322     if(renderTo){
27323         this.render(renderTo);
27324     }
27325     
27326   
27327 };
27328
27329 Roo.extend(Roo.Button, Roo.util.Observable, {
27330     /**
27331      * 
27332      */
27333     
27334     /**
27335      * Read-only. True if this button is hidden
27336      * @type Boolean
27337      */
27338     hidden : false,
27339     /**
27340      * Read-only. True if this button is disabled
27341      * @type Boolean
27342      */
27343     disabled : false,
27344     /**
27345      * Read-only. True if this button is pressed (only if enableToggle = true)
27346      * @type Boolean
27347      */
27348     pressed : false,
27349
27350     /**
27351      * @cfg {Number} tabIndex 
27352      * The DOM tabIndex for this button (defaults to undefined)
27353      */
27354     tabIndex : undefined,
27355
27356     /**
27357      * @cfg {Boolean} enableToggle
27358      * True to enable pressed/not pressed toggling (defaults to false)
27359      */
27360     enableToggle: false,
27361     /**
27362      * @cfg {Mixed} menu
27363      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27364      */
27365     menu : undefined,
27366     /**
27367      * @cfg {String} menuAlign
27368      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27369      */
27370     menuAlign : "tl-bl?",
27371
27372     /**
27373      * @cfg {String} iconCls
27374      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27375      */
27376     iconCls : undefined,
27377     /**
27378      * @cfg {String} type
27379      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27380      */
27381     type : 'button',
27382
27383     // private
27384     menuClassTarget: 'tr',
27385
27386     /**
27387      * @cfg {String} clickEvent
27388      * The type of event to map to the button's event handler (defaults to 'click')
27389      */
27390     clickEvent : 'click',
27391
27392     /**
27393      * @cfg {Boolean} handleMouseEvents
27394      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27395      */
27396     handleMouseEvents : true,
27397
27398     /**
27399      * @cfg {String} tooltipType
27400      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27401      */
27402     tooltipType : 'qtip',
27403
27404     /**
27405      * @cfg {String} cls
27406      * A CSS class to apply to the button's main element.
27407      */
27408     
27409     /**
27410      * @cfg {Roo.Template} template (Optional)
27411      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27412      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27413      * require code modifications if required elements (e.g. a button) aren't present.
27414      */
27415
27416     // private
27417     render : function(renderTo){
27418         var btn;
27419         if(this.hideParent){
27420             this.parentEl = Roo.get(renderTo);
27421         }
27422         if(!this.dhconfig){
27423             if(!this.template){
27424                 if(!Roo.Button.buttonTemplate){
27425                     // hideous table template
27426                     Roo.Button.buttonTemplate = new Roo.Template(
27427                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27428                         '<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>',
27429                         "</tr></tbody></table>");
27430                 }
27431                 this.template = Roo.Button.buttonTemplate;
27432             }
27433             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27434             var btnEl = btn.child("button:first");
27435             btnEl.on('focus', this.onFocus, this);
27436             btnEl.on('blur', this.onBlur, this);
27437             if(this.cls){
27438                 btn.addClass(this.cls);
27439             }
27440             if(this.icon){
27441                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27442             }
27443             if(this.iconCls){
27444                 btnEl.addClass(this.iconCls);
27445                 if(!this.cls){
27446                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27447                 }
27448             }
27449             if(this.tabIndex !== undefined){
27450                 btnEl.dom.tabIndex = this.tabIndex;
27451             }
27452             if(this.tooltip){
27453                 if(typeof this.tooltip == 'object'){
27454                     Roo.QuickTips.tips(Roo.apply({
27455                           target: btnEl.id
27456                     }, this.tooltip));
27457                 } else {
27458                     btnEl.dom[this.tooltipType] = this.tooltip;
27459                 }
27460             }
27461         }else{
27462             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27463         }
27464         this.el = btn;
27465         if(this.id){
27466             this.el.dom.id = this.el.id = this.id;
27467         }
27468         if(this.menu){
27469             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27470             this.menu.on("show", this.onMenuShow, this);
27471             this.menu.on("hide", this.onMenuHide, this);
27472         }
27473         btn.addClass("x-btn");
27474         if(Roo.isIE && !Roo.isIE7){
27475             this.autoWidth.defer(1, this);
27476         }else{
27477             this.autoWidth();
27478         }
27479         if(this.handleMouseEvents){
27480             btn.on("mouseover", this.onMouseOver, this);
27481             btn.on("mouseout", this.onMouseOut, this);
27482             btn.on("mousedown", this.onMouseDown, this);
27483         }
27484         btn.on(this.clickEvent, this.onClick, this);
27485         //btn.on("mouseup", this.onMouseUp, this);
27486         if(this.hidden){
27487             this.hide();
27488         }
27489         if(this.disabled){
27490             this.disable();
27491         }
27492         Roo.ButtonToggleMgr.register(this);
27493         if(this.pressed){
27494             this.el.addClass("x-btn-pressed");
27495         }
27496         if(this.repeat){
27497             var repeater = new Roo.util.ClickRepeater(btn,
27498                 typeof this.repeat == "object" ? this.repeat : {}
27499             );
27500             repeater.on("click", this.onClick,  this);
27501         }
27502         
27503         this.fireEvent('render', this);
27504         
27505     },
27506     /**
27507      * Returns the button's underlying element
27508      * @return {Roo.Element} The element
27509      */
27510     getEl : function(){
27511         return this.el;  
27512     },
27513     
27514     /**
27515      * Destroys this Button and removes any listeners.
27516      */
27517     destroy : function(){
27518         Roo.ButtonToggleMgr.unregister(this);
27519         this.el.removeAllListeners();
27520         this.purgeListeners();
27521         this.el.remove();
27522     },
27523
27524     // private
27525     autoWidth : function(){
27526         if(this.el){
27527             this.el.setWidth("auto");
27528             if(Roo.isIE7 && Roo.isStrict){
27529                 var ib = this.el.child('button');
27530                 if(ib && ib.getWidth() > 20){
27531                     ib.clip();
27532                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27533                 }
27534             }
27535             if(this.minWidth){
27536                 if(this.hidden){
27537                     this.el.beginMeasure();
27538                 }
27539                 if(this.el.getWidth() < this.minWidth){
27540                     this.el.setWidth(this.minWidth);
27541                 }
27542                 if(this.hidden){
27543                     this.el.endMeasure();
27544                 }
27545             }
27546         }
27547     },
27548
27549     /**
27550      * Assigns this button's click handler
27551      * @param {Function} handler The function to call when the button is clicked
27552      * @param {Object} scope (optional) Scope for the function passed in
27553      */
27554     setHandler : function(handler, scope){
27555         this.handler = handler;
27556         this.scope = scope;  
27557     },
27558     
27559     /**
27560      * Sets this button's text
27561      * @param {String} text The button text
27562      */
27563     setText : function(text){
27564         this.text = text;
27565         if(this.el){
27566             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27567         }
27568         this.autoWidth();
27569     },
27570     
27571     /**
27572      * Gets the text for this button
27573      * @return {String} The button text
27574      */
27575     getText : function(){
27576         return this.text;  
27577     },
27578     
27579     /**
27580      * Show this button
27581      */
27582     show: function(){
27583         this.hidden = false;
27584         if(this.el){
27585             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27586         }
27587     },
27588     
27589     /**
27590      * Hide this button
27591      */
27592     hide: function(){
27593         this.hidden = true;
27594         if(this.el){
27595             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27596         }
27597     },
27598     
27599     /**
27600      * Convenience function for boolean show/hide
27601      * @param {Boolean} visible True to show, false to hide
27602      */
27603     setVisible: function(visible){
27604         if(visible) {
27605             this.show();
27606         }else{
27607             this.hide();
27608         }
27609     },
27610     
27611     /**
27612      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27613      * @param {Boolean} state (optional) Force a particular state
27614      */
27615     toggle : function(state){
27616         state = state === undefined ? !this.pressed : state;
27617         if(state != this.pressed){
27618             if(state){
27619                 this.el.addClass("x-btn-pressed");
27620                 this.pressed = true;
27621                 this.fireEvent("toggle", this, true);
27622             }else{
27623                 this.el.removeClass("x-btn-pressed");
27624                 this.pressed = false;
27625                 this.fireEvent("toggle", this, false);
27626             }
27627             if(this.toggleHandler){
27628                 this.toggleHandler.call(this.scope || this, this, state);
27629             }
27630         }
27631     },
27632     
27633     /**
27634      * Focus the button
27635      */
27636     focus : function(){
27637         this.el.child('button:first').focus();
27638     },
27639     
27640     /**
27641      * Disable this button
27642      */
27643     disable : function(){
27644         if(this.el){
27645             this.el.addClass("x-btn-disabled");
27646         }
27647         this.disabled = true;
27648     },
27649     
27650     /**
27651      * Enable this button
27652      */
27653     enable : function(){
27654         if(this.el){
27655             this.el.removeClass("x-btn-disabled");
27656         }
27657         this.disabled = false;
27658     },
27659
27660     /**
27661      * Convenience function for boolean enable/disable
27662      * @param {Boolean} enabled True to enable, false to disable
27663      */
27664     setDisabled : function(v){
27665         this[v !== true ? "enable" : "disable"]();
27666     },
27667
27668     // private
27669     onClick : function(e){
27670         if(e){
27671             e.preventDefault();
27672         }
27673         if(e.button != 0){
27674             return;
27675         }
27676         if(!this.disabled){
27677             if(this.enableToggle){
27678                 this.toggle();
27679             }
27680             if(this.menu && !this.menu.isVisible()){
27681                 this.menu.show(this.el, this.menuAlign);
27682             }
27683             this.fireEvent("click", this, e);
27684             if(this.handler){
27685                 this.el.removeClass("x-btn-over");
27686                 this.handler.call(this.scope || this, this, e);
27687             }
27688         }
27689     },
27690     // private
27691     onMouseOver : function(e){
27692         if(!this.disabled){
27693             this.el.addClass("x-btn-over");
27694             this.fireEvent('mouseover', this, e);
27695         }
27696     },
27697     // private
27698     onMouseOut : function(e){
27699         if(!e.within(this.el,  true)){
27700             this.el.removeClass("x-btn-over");
27701             this.fireEvent('mouseout', this, e);
27702         }
27703     },
27704     // private
27705     onFocus : function(e){
27706         if(!this.disabled){
27707             this.el.addClass("x-btn-focus");
27708         }
27709     },
27710     // private
27711     onBlur : function(e){
27712         this.el.removeClass("x-btn-focus");
27713     },
27714     // private
27715     onMouseDown : function(e){
27716         if(!this.disabled && e.button == 0){
27717             this.el.addClass("x-btn-click");
27718             Roo.get(document).on('mouseup', this.onMouseUp, this);
27719         }
27720     },
27721     // private
27722     onMouseUp : function(e){
27723         if(e.button == 0){
27724             this.el.removeClass("x-btn-click");
27725             Roo.get(document).un('mouseup', this.onMouseUp, this);
27726         }
27727     },
27728     // private
27729     onMenuShow : function(e){
27730         this.el.addClass("x-btn-menu-active");
27731     },
27732     // private
27733     onMenuHide : function(e){
27734         this.el.removeClass("x-btn-menu-active");
27735     }   
27736 });
27737
27738 // Private utility class used by Button
27739 Roo.ButtonToggleMgr = function(){
27740    var groups = {};
27741    
27742    function toggleGroup(btn, state){
27743        if(state){
27744            var g = groups[btn.toggleGroup];
27745            for(var i = 0, l = g.length; i < l; i++){
27746                if(g[i] != btn){
27747                    g[i].toggle(false);
27748                }
27749            }
27750        }
27751    }
27752    
27753    return {
27754        register : function(btn){
27755            if(!btn.toggleGroup){
27756                return;
27757            }
27758            var g = groups[btn.toggleGroup];
27759            if(!g){
27760                g = groups[btn.toggleGroup] = [];
27761            }
27762            g.push(btn);
27763            btn.on("toggle", toggleGroup);
27764        },
27765        
27766        unregister : function(btn){
27767            if(!btn.toggleGroup){
27768                return;
27769            }
27770            var g = groups[btn.toggleGroup];
27771            if(g){
27772                g.remove(btn);
27773                btn.un("toggle", toggleGroup);
27774            }
27775        }
27776    };
27777 }();/*
27778  * Based on:
27779  * Ext JS Library 1.1.1
27780  * Copyright(c) 2006-2007, Ext JS, LLC.
27781  *
27782  * Originally Released Under LGPL - original licence link has changed is not relivant.
27783  *
27784  * Fork - LGPL
27785  * <script type="text/javascript">
27786  */
27787  
27788 /**
27789  * @class Roo.SplitButton
27790  * @extends Roo.Button
27791  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27792  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27793  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27794  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27795  * @cfg {String} arrowTooltip The title attribute of the arrow
27796  * @constructor
27797  * Create a new menu button
27798  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27799  * @param {Object} config The config object
27800  */
27801 Roo.SplitButton = function(renderTo, config){
27802     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27803     /**
27804      * @event arrowclick
27805      * Fires when this button's arrow is clicked
27806      * @param {SplitButton} this
27807      * @param {EventObject} e The click event
27808      */
27809     this.addEvents({"arrowclick":true});
27810 };
27811
27812 Roo.extend(Roo.SplitButton, Roo.Button, {
27813     render : function(renderTo){
27814         // this is one sweet looking template!
27815         var tpl = new Roo.Template(
27816             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27817             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27818             '<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>',
27819             "</tbody></table></td><td>",
27820             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27821             '<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>',
27822             "</tbody></table></td></tr></table>"
27823         );
27824         var btn = tpl.append(renderTo, [this.text, this.type], true);
27825         var btnEl = btn.child("button");
27826         if(this.cls){
27827             btn.addClass(this.cls);
27828         }
27829         if(this.icon){
27830             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27831         }
27832         if(this.iconCls){
27833             btnEl.addClass(this.iconCls);
27834             if(!this.cls){
27835                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27836             }
27837         }
27838         this.el = btn;
27839         if(this.handleMouseEvents){
27840             btn.on("mouseover", this.onMouseOver, this);
27841             btn.on("mouseout", this.onMouseOut, this);
27842             btn.on("mousedown", this.onMouseDown, this);
27843             btn.on("mouseup", this.onMouseUp, this);
27844         }
27845         btn.on(this.clickEvent, this.onClick, this);
27846         if(this.tooltip){
27847             if(typeof this.tooltip == 'object'){
27848                 Roo.QuickTips.tips(Roo.apply({
27849                       target: btnEl.id
27850                 }, this.tooltip));
27851             } else {
27852                 btnEl.dom[this.tooltipType] = this.tooltip;
27853             }
27854         }
27855         if(this.arrowTooltip){
27856             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27857         }
27858         if(this.hidden){
27859             this.hide();
27860         }
27861         if(this.disabled){
27862             this.disable();
27863         }
27864         if(this.pressed){
27865             this.el.addClass("x-btn-pressed");
27866         }
27867         if(Roo.isIE && !Roo.isIE7){
27868             this.autoWidth.defer(1, this);
27869         }else{
27870             this.autoWidth();
27871         }
27872         if(this.menu){
27873             this.menu.on("show", this.onMenuShow, this);
27874             this.menu.on("hide", this.onMenuHide, this);
27875         }
27876         this.fireEvent('render', this);
27877     },
27878
27879     // private
27880     autoWidth : function(){
27881         if(this.el){
27882             var tbl = this.el.child("table:first");
27883             var tbl2 = this.el.child("table:last");
27884             this.el.setWidth("auto");
27885             tbl.setWidth("auto");
27886             if(Roo.isIE7 && Roo.isStrict){
27887                 var ib = this.el.child('button:first');
27888                 if(ib && ib.getWidth() > 20){
27889                     ib.clip();
27890                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27891                 }
27892             }
27893             if(this.minWidth){
27894                 if(this.hidden){
27895                     this.el.beginMeasure();
27896                 }
27897                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27898                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27899                 }
27900                 if(this.hidden){
27901                     this.el.endMeasure();
27902                 }
27903             }
27904             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27905         } 
27906     },
27907     /**
27908      * Sets this button's click handler
27909      * @param {Function} handler The function to call when the button is clicked
27910      * @param {Object} scope (optional) Scope for the function passed above
27911      */
27912     setHandler : function(handler, scope){
27913         this.handler = handler;
27914         this.scope = scope;  
27915     },
27916     
27917     /**
27918      * Sets this button's arrow click handler
27919      * @param {Function} handler The function to call when the arrow is clicked
27920      * @param {Object} scope (optional) Scope for the function passed above
27921      */
27922     setArrowHandler : function(handler, scope){
27923         this.arrowHandler = handler;
27924         this.scope = scope;  
27925     },
27926     
27927     /**
27928      * Focus the button
27929      */
27930     focus : function(){
27931         if(this.el){
27932             this.el.child("button:first").focus();
27933         }
27934     },
27935
27936     // private
27937     onClick : function(e){
27938         e.preventDefault();
27939         if(!this.disabled){
27940             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27941                 if(this.menu && !this.menu.isVisible()){
27942                     this.menu.show(this.el, this.menuAlign);
27943                 }
27944                 this.fireEvent("arrowclick", this, e);
27945                 if(this.arrowHandler){
27946                     this.arrowHandler.call(this.scope || this, this, e);
27947                 }
27948             }else{
27949                 this.fireEvent("click", this, e);
27950                 if(this.handler){
27951                     this.handler.call(this.scope || this, this, e);
27952                 }
27953             }
27954         }
27955     },
27956     // private
27957     onMouseDown : function(e){
27958         if(!this.disabled){
27959             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27960         }
27961     },
27962     // private
27963     onMouseUp : function(e){
27964         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27965     }   
27966 });
27967
27968
27969 // backwards compat
27970 Roo.MenuButton = Roo.SplitButton;/*
27971  * Based on:
27972  * Ext JS Library 1.1.1
27973  * Copyright(c) 2006-2007, Ext JS, LLC.
27974  *
27975  * Originally Released Under LGPL - original licence link has changed is not relivant.
27976  *
27977  * Fork - LGPL
27978  * <script type="text/javascript">
27979  */
27980
27981 /**
27982  * @class Roo.Toolbar
27983  * Basic Toolbar class.
27984  * @constructor
27985  * Creates a new Toolbar
27986  * @param {Object} container The config object
27987  */ 
27988 Roo.Toolbar = function(container, buttons, config)
27989 {
27990     /// old consturctor format still supported..
27991     if(container instanceof Array){ // omit the container for later rendering
27992         buttons = container;
27993         config = buttons;
27994         container = null;
27995     }
27996     if (typeof(container) == 'object' && container.xtype) {
27997         config = container;
27998         container = config.container;
27999         buttons = config.buttons || []; // not really - use items!!
28000     }
28001     var xitems = [];
28002     if (config && config.items) {
28003         xitems = config.items;
28004         delete config.items;
28005     }
28006     Roo.apply(this, config);
28007     this.buttons = buttons;
28008     
28009     if(container){
28010         this.render(container);
28011     }
28012     this.xitems = xitems;
28013     Roo.each(xitems, function(b) {
28014         this.add(b);
28015     }, this);
28016     
28017 };
28018
28019 Roo.Toolbar.prototype = {
28020     /**
28021      * @cfg {Array} items
28022      * array of button configs or elements to add (will be converted to a MixedCollection)
28023      */
28024     
28025     /**
28026      * @cfg {String/HTMLElement/Element} container
28027      * The id or element that will contain the toolbar
28028      */
28029     // private
28030     render : function(ct){
28031         this.el = Roo.get(ct);
28032         if(this.cls){
28033             this.el.addClass(this.cls);
28034         }
28035         // using a table allows for vertical alignment
28036         // 100% width is needed by Safari...
28037         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28038         this.tr = this.el.child("tr", true);
28039         var autoId = 0;
28040         this.items = new Roo.util.MixedCollection(false, function(o){
28041             return o.id || ("item" + (++autoId));
28042         });
28043         if(this.buttons){
28044             this.add.apply(this, this.buttons);
28045             delete this.buttons;
28046         }
28047     },
28048
28049     /**
28050      * Adds element(s) to the toolbar -- this function takes a variable number of 
28051      * arguments of mixed type and adds them to the toolbar.
28052      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28053      * <ul>
28054      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28055      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28056      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28057      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28058      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28059      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28060      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28061      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28062      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28063      * </ul>
28064      * @param {Mixed} arg2
28065      * @param {Mixed} etc.
28066      */
28067     add : function(){
28068         var a = arguments, l = a.length;
28069         for(var i = 0; i < l; i++){
28070             this._add(a[i]);
28071         }
28072     },
28073     // private..
28074     _add : function(el) {
28075         
28076         if (el.xtype) {
28077             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28078         }
28079         
28080         if (el.applyTo){ // some kind of form field
28081             return this.addField(el);
28082         } 
28083         if (el.render){ // some kind of Toolbar.Item
28084             return this.addItem(el);
28085         }
28086         if (typeof el == "string"){ // string
28087             if(el == "separator" || el == "-"){
28088                 return this.addSeparator();
28089             }
28090             if (el == " "){
28091                 return this.addSpacer();
28092             }
28093             if(el == "->"){
28094                 return this.addFill();
28095             }
28096             return this.addText(el);
28097             
28098         }
28099         if(el.tagName){ // element
28100             return this.addElement(el);
28101         }
28102         if(typeof el == "object"){ // must be button config?
28103             return this.addButton(el);
28104         }
28105         // and now what?!?!
28106         return false;
28107         
28108     },
28109     
28110     /**
28111      * Add an Xtype element
28112      * @param {Object} xtype Xtype Object
28113      * @return {Object} created Object
28114      */
28115     addxtype : function(e){
28116         return this.add(e);  
28117     },
28118     
28119     /**
28120      * Returns the Element for this toolbar.
28121      * @return {Roo.Element}
28122      */
28123     getEl : function(){
28124         return this.el;  
28125     },
28126     
28127     /**
28128      * Adds a separator
28129      * @return {Roo.Toolbar.Item} The separator item
28130      */
28131     addSeparator : function(){
28132         return this.addItem(new Roo.Toolbar.Separator());
28133     },
28134
28135     /**
28136      * Adds a spacer element
28137      * @return {Roo.Toolbar.Spacer} The spacer item
28138      */
28139     addSpacer : function(){
28140         return this.addItem(new Roo.Toolbar.Spacer());
28141     },
28142
28143     /**
28144      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28145      * @return {Roo.Toolbar.Fill} The fill item
28146      */
28147     addFill : function(){
28148         return this.addItem(new Roo.Toolbar.Fill());
28149     },
28150
28151     /**
28152      * Adds any standard HTML element to the toolbar
28153      * @param {String/HTMLElement/Element} el The element or id of the element to add
28154      * @return {Roo.Toolbar.Item} The element's item
28155      */
28156     addElement : function(el){
28157         return this.addItem(new Roo.Toolbar.Item(el));
28158     },
28159     /**
28160      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28161      * @type Roo.util.MixedCollection  
28162      */
28163     items : false,
28164      
28165     /**
28166      * Adds any Toolbar.Item or subclass
28167      * @param {Roo.Toolbar.Item} item
28168      * @return {Roo.Toolbar.Item} The item
28169      */
28170     addItem : function(item){
28171         var td = this.nextBlock();
28172         item.render(td);
28173         this.items.add(item);
28174         return item;
28175     },
28176     
28177     /**
28178      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28179      * @param {Object/Array} config A button config or array of configs
28180      * @return {Roo.Toolbar.Button/Array}
28181      */
28182     addButton : function(config){
28183         if(config instanceof Array){
28184             var buttons = [];
28185             for(var i = 0, len = config.length; i < len; i++) {
28186                 buttons.push(this.addButton(config[i]));
28187             }
28188             return buttons;
28189         }
28190         var b = config;
28191         if(!(config instanceof Roo.Toolbar.Button)){
28192             b = config.split ?
28193                 new Roo.Toolbar.SplitButton(config) :
28194                 new Roo.Toolbar.Button(config);
28195         }
28196         var td = this.nextBlock();
28197         b.render(td);
28198         this.items.add(b);
28199         return b;
28200     },
28201     
28202     /**
28203      * Adds text to the toolbar
28204      * @param {String} text The text to add
28205      * @return {Roo.Toolbar.Item} The element's item
28206      */
28207     addText : function(text){
28208         return this.addItem(new Roo.Toolbar.TextItem(text));
28209     },
28210     
28211     /**
28212      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28213      * @param {Number} index The index where the item is to be inserted
28214      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28215      * @return {Roo.Toolbar.Button/Item}
28216      */
28217     insertButton : function(index, item){
28218         if(item instanceof Array){
28219             var buttons = [];
28220             for(var i = 0, len = item.length; i < len; i++) {
28221                buttons.push(this.insertButton(index + i, item[i]));
28222             }
28223             return buttons;
28224         }
28225         if (!(item instanceof Roo.Toolbar.Button)){
28226            item = new Roo.Toolbar.Button(item);
28227         }
28228         var td = document.createElement("td");
28229         this.tr.insertBefore(td, this.tr.childNodes[index]);
28230         item.render(td);
28231         this.items.insert(index, item);
28232         return item;
28233     },
28234     
28235     /**
28236      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28237      * @param {Object} config
28238      * @return {Roo.Toolbar.Item} The element's item
28239      */
28240     addDom : function(config, returnEl){
28241         var td = this.nextBlock();
28242         Roo.DomHelper.overwrite(td, config);
28243         var ti = new Roo.Toolbar.Item(td.firstChild);
28244         ti.render(td);
28245         this.items.add(ti);
28246         return ti;
28247     },
28248
28249     /**
28250      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28251      * @type Roo.util.MixedCollection  
28252      */
28253     fields : false,
28254     
28255     /**
28256      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28257      * Note: the field should not have been rendered yet. For a field that has already been
28258      * rendered, use {@link #addElement}.
28259      * @param {Roo.form.Field} field
28260      * @return {Roo.ToolbarItem}
28261      */
28262      
28263       
28264     addField : function(field) {
28265         if (!this.fields) {
28266             var autoId = 0;
28267             this.fields = new Roo.util.MixedCollection(false, function(o){
28268                 return o.id || ("item" + (++autoId));
28269             });
28270
28271         }
28272         
28273         var td = this.nextBlock();
28274         field.render(td);
28275         var ti = new Roo.Toolbar.Item(td.firstChild);
28276         ti.render(td);
28277         this.items.add(ti);
28278         this.fields.add(field);
28279         return ti;
28280     },
28281     /**
28282      * Hide the toolbar
28283      * @method hide
28284      */
28285      
28286       
28287     hide : function()
28288     {
28289         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28290         this.el.child('div').hide();
28291     },
28292     /**
28293      * Show the toolbar
28294      * @method show
28295      */
28296     show : function()
28297     {
28298         this.el.child('div').show();
28299     },
28300       
28301     // private
28302     nextBlock : function(){
28303         var td = document.createElement("td");
28304         this.tr.appendChild(td);
28305         return td;
28306     },
28307
28308     // private
28309     destroy : function(){
28310         if(this.items){ // rendered?
28311             Roo.destroy.apply(Roo, this.items.items);
28312         }
28313         if(this.fields){ // rendered?
28314             Roo.destroy.apply(Roo, this.fields.items);
28315         }
28316         Roo.Element.uncache(this.el, this.tr);
28317     }
28318 };
28319
28320 /**
28321  * @class Roo.Toolbar.Item
28322  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28323  * @constructor
28324  * Creates a new Item
28325  * @param {HTMLElement} el 
28326  */
28327 Roo.Toolbar.Item = function(el){
28328     this.el = Roo.getDom(el);
28329     this.id = Roo.id(this.el);
28330     this.hidden = false;
28331 };
28332
28333 Roo.Toolbar.Item.prototype = {
28334     
28335     /**
28336      * Get this item's HTML Element
28337      * @return {HTMLElement}
28338      */
28339     getEl : function(){
28340        return this.el;  
28341     },
28342
28343     // private
28344     render : function(td){
28345         this.td = td;
28346         td.appendChild(this.el);
28347     },
28348     
28349     /**
28350      * Removes and destroys this item.
28351      */
28352     destroy : function(){
28353         this.td.parentNode.removeChild(this.td);
28354     },
28355     
28356     /**
28357      * Shows this item.
28358      */
28359     show: function(){
28360         this.hidden = false;
28361         this.td.style.display = "";
28362     },
28363     
28364     /**
28365      * Hides this item.
28366      */
28367     hide: function(){
28368         this.hidden = true;
28369         this.td.style.display = "none";
28370     },
28371     
28372     /**
28373      * Convenience function for boolean show/hide.
28374      * @param {Boolean} visible true to show/false to hide
28375      */
28376     setVisible: function(visible){
28377         if(visible) {
28378             this.show();
28379         }else{
28380             this.hide();
28381         }
28382     },
28383     
28384     /**
28385      * Try to focus this item.
28386      */
28387     focus : function(){
28388         Roo.fly(this.el).focus();
28389     },
28390     
28391     /**
28392      * Disables this item.
28393      */
28394     disable : function(){
28395         Roo.fly(this.td).addClass("x-item-disabled");
28396         this.disabled = true;
28397         this.el.disabled = true;
28398     },
28399     
28400     /**
28401      * Enables this item.
28402      */
28403     enable : function(){
28404         Roo.fly(this.td).removeClass("x-item-disabled");
28405         this.disabled = false;
28406         this.el.disabled = false;
28407     }
28408 };
28409
28410
28411 /**
28412  * @class Roo.Toolbar.Separator
28413  * @extends Roo.Toolbar.Item
28414  * A simple toolbar separator class
28415  * @constructor
28416  * Creates a new Separator
28417  */
28418 Roo.Toolbar.Separator = function(){
28419     var s = document.createElement("span");
28420     s.className = "ytb-sep";
28421     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28422 };
28423 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28424     enable:Roo.emptyFn,
28425     disable:Roo.emptyFn,
28426     focus:Roo.emptyFn
28427 });
28428
28429 /**
28430  * @class Roo.Toolbar.Spacer
28431  * @extends Roo.Toolbar.Item
28432  * A simple element that adds extra horizontal space to a toolbar.
28433  * @constructor
28434  * Creates a new Spacer
28435  */
28436 Roo.Toolbar.Spacer = function(){
28437     var s = document.createElement("div");
28438     s.className = "ytb-spacer";
28439     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28440 };
28441 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28442     enable:Roo.emptyFn,
28443     disable:Roo.emptyFn,
28444     focus:Roo.emptyFn
28445 });
28446
28447 /**
28448  * @class Roo.Toolbar.Fill
28449  * @extends Roo.Toolbar.Spacer
28450  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28451  * @constructor
28452  * Creates a new Spacer
28453  */
28454 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28455     // private
28456     render : function(td){
28457         td.style.width = '100%';
28458         Roo.Toolbar.Fill.superclass.render.call(this, td);
28459     }
28460 });
28461
28462 /**
28463  * @class Roo.Toolbar.TextItem
28464  * @extends Roo.Toolbar.Item
28465  * A simple class that renders text directly into a toolbar.
28466  * @constructor
28467  * Creates a new TextItem
28468  * @param {String} text
28469  */
28470 Roo.Toolbar.TextItem = function(text){
28471     if (typeof(text) == 'object') {
28472         text = text.text;
28473     }
28474     var s = document.createElement("span");
28475     s.className = "ytb-text";
28476     s.innerHTML = text;
28477     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28478 };
28479 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28480     enable:Roo.emptyFn,
28481     disable:Roo.emptyFn,
28482     focus:Roo.emptyFn
28483 });
28484
28485 /**
28486  * @class Roo.Toolbar.Button
28487  * @extends Roo.Button
28488  * A button that renders into a toolbar.
28489  * @constructor
28490  * Creates a new Button
28491  * @param {Object} config A standard {@link Roo.Button} config object
28492  */
28493 Roo.Toolbar.Button = function(config){
28494     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28495 };
28496 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28497     render : function(td){
28498         this.td = td;
28499         Roo.Toolbar.Button.superclass.render.call(this, td);
28500     },
28501     
28502     /**
28503      * Removes and destroys this button
28504      */
28505     destroy : function(){
28506         Roo.Toolbar.Button.superclass.destroy.call(this);
28507         this.td.parentNode.removeChild(this.td);
28508     },
28509     
28510     /**
28511      * Shows this button
28512      */
28513     show: function(){
28514         this.hidden = false;
28515         this.td.style.display = "";
28516     },
28517     
28518     /**
28519      * Hides this button
28520      */
28521     hide: function(){
28522         this.hidden = true;
28523         this.td.style.display = "none";
28524     },
28525
28526     /**
28527      * Disables this item
28528      */
28529     disable : function(){
28530         Roo.fly(this.td).addClass("x-item-disabled");
28531         this.disabled = true;
28532     },
28533
28534     /**
28535      * Enables this item
28536      */
28537     enable : function(){
28538         Roo.fly(this.td).removeClass("x-item-disabled");
28539         this.disabled = false;
28540     }
28541 });
28542 // backwards compat
28543 Roo.ToolbarButton = Roo.Toolbar.Button;
28544
28545 /**
28546  * @class Roo.Toolbar.SplitButton
28547  * @extends Roo.SplitButton
28548  * A menu button that renders into a toolbar.
28549  * @constructor
28550  * Creates a new SplitButton
28551  * @param {Object} config A standard {@link Roo.SplitButton} config object
28552  */
28553 Roo.Toolbar.SplitButton = function(config){
28554     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28555 };
28556 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28557     render : function(td){
28558         this.td = td;
28559         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28560     },
28561     
28562     /**
28563      * Removes and destroys this button
28564      */
28565     destroy : function(){
28566         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28567         this.td.parentNode.removeChild(this.td);
28568     },
28569     
28570     /**
28571      * Shows this button
28572      */
28573     show: function(){
28574         this.hidden = false;
28575         this.td.style.display = "";
28576     },
28577     
28578     /**
28579      * Hides this button
28580      */
28581     hide: function(){
28582         this.hidden = true;
28583         this.td.style.display = "none";
28584     }
28585 });
28586
28587 // backwards compat
28588 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28589  * Based on:
28590  * Ext JS Library 1.1.1
28591  * Copyright(c) 2006-2007, Ext JS, LLC.
28592  *
28593  * Originally Released Under LGPL - original licence link has changed is not relivant.
28594  *
28595  * Fork - LGPL
28596  * <script type="text/javascript">
28597  */
28598  
28599 /**
28600  * @class Roo.PagingToolbar
28601  * @extends Roo.Toolbar
28602  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28603  * @constructor
28604  * Create a new PagingToolbar
28605  * @param {Object} config The config object
28606  */
28607 Roo.PagingToolbar = function(el, ds, config)
28608 {
28609     // old args format still supported... - xtype is prefered..
28610     if (typeof(el) == 'object' && el.xtype) {
28611         // created from xtype...
28612         config = el;
28613         ds = el.dataSource;
28614         el = config.container;
28615     }
28616     var items = [];
28617     if (config.items) {
28618         items = config.items;
28619         config.items = [];
28620     }
28621     
28622     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28623     this.ds = ds;
28624     this.cursor = 0;
28625     this.renderButtons(this.el);
28626     this.bind(ds);
28627     
28628     // supprot items array.
28629    
28630     Roo.each(items, function(e) {
28631         this.add(Roo.factory(e));
28632     },this);
28633     
28634 };
28635
28636 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28637     /**
28638      * @cfg {Roo.data.Store} dataSource
28639      * The underlying data store providing the paged data
28640      */
28641     /**
28642      * @cfg {String/HTMLElement/Element} container
28643      * container The id or element that will contain the toolbar
28644      */
28645     /**
28646      * @cfg {Boolean} displayInfo
28647      * True to display the displayMsg (defaults to false)
28648      */
28649     /**
28650      * @cfg {Number} pageSize
28651      * The number of records to display per page (defaults to 20)
28652      */
28653     pageSize: 20,
28654     /**
28655      * @cfg {String} displayMsg
28656      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28657      */
28658     displayMsg : 'Displaying {0} - {1} of {2}',
28659     /**
28660      * @cfg {String} emptyMsg
28661      * The message to display when no records are found (defaults to "No data to display")
28662      */
28663     emptyMsg : 'No data to display',
28664     /**
28665      * Customizable piece of the default paging text (defaults to "Page")
28666      * @type String
28667      */
28668     beforePageText : "Page",
28669     /**
28670      * Customizable piece of the default paging text (defaults to "of %0")
28671      * @type String
28672      */
28673     afterPageText : "of {0}",
28674     /**
28675      * Customizable piece of the default paging text (defaults to "First Page")
28676      * @type String
28677      */
28678     firstText : "First Page",
28679     /**
28680      * Customizable piece of the default paging text (defaults to "Previous Page")
28681      * @type String
28682      */
28683     prevText : "Previous Page",
28684     /**
28685      * Customizable piece of the default paging text (defaults to "Next Page")
28686      * @type String
28687      */
28688     nextText : "Next Page",
28689     /**
28690      * Customizable piece of the default paging text (defaults to "Last Page")
28691      * @type String
28692      */
28693     lastText : "Last Page",
28694     /**
28695      * Customizable piece of the default paging text (defaults to "Refresh")
28696      * @type String
28697      */
28698     refreshText : "Refresh",
28699
28700     // private
28701     renderButtons : function(el){
28702         Roo.PagingToolbar.superclass.render.call(this, el);
28703         this.first = this.addButton({
28704             tooltip: this.firstText,
28705             cls: "x-btn-icon x-grid-page-first",
28706             disabled: true,
28707             handler: this.onClick.createDelegate(this, ["first"])
28708         });
28709         this.prev = this.addButton({
28710             tooltip: this.prevText,
28711             cls: "x-btn-icon x-grid-page-prev",
28712             disabled: true,
28713             handler: this.onClick.createDelegate(this, ["prev"])
28714         });
28715         //this.addSeparator();
28716         this.add(this.beforePageText);
28717         this.field = Roo.get(this.addDom({
28718            tag: "input",
28719            type: "text",
28720            size: "3",
28721            value: "1",
28722            cls: "x-grid-page-number"
28723         }).el);
28724         this.field.on("keydown", this.onPagingKeydown, this);
28725         this.field.on("focus", function(){this.dom.select();});
28726         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28727         this.field.setHeight(18);
28728         //this.addSeparator();
28729         this.next = this.addButton({
28730             tooltip: this.nextText,
28731             cls: "x-btn-icon x-grid-page-next",
28732             disabled: true,
28733             handler: this.onClick.createDelegate(this, ["next"])
28734         });
28735         this.last = this.addButton({
28736             tooltip: this.lastText,
28737             cls: "x-btn-icon x-grid-page-last",
28738             disabled: true,
28739             handler: this.onClick.createDelegate(this, ["last"])
28740         });
28741         //this.addSeparator();
28742         this.loading = this.addButton({
28743             tooltip: this.refreshText,
28744             cls: "x-btn-icon x-grid-loading",
28745             handler: this.onClick.createDelegate(this, ["refresh"])
28746         });
28747
28748         if(this.displayInfo){
28749             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28750         }
28751     },
28752
28753     // private
28754     updateInfo : function(){
28755         if(this.displayEl){
28756             var count = this.ds.getCount();
28757             var msg = count == 0 ?
28758                 this.emptyMsg :
28759                 String.format(
28760                     this.displayMsg,
28761                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28762                 );
28763             this.displayEl.update(msg);
28764         }
28765     },
28766
28767     // private
28768     onLoad : function(ds, r, o){
28769        this.cursor = o.params ? o.params.start : 0;
28770        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28771
28772        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28773        this.field.dom.value = ap;
28774        this.first.setDisabled(ap == 1);
28775        this.prev.setDisabled(ap == 1);
28776        this.next.setDisabled(ap == ps);
28777        this.last.setDisabled(ap == ps);
28778        this.loading.enable();
28779        this.updateInfo();
28780     },
28781
28782     // private
28783     getPageData : function(){
28784         var total = this.ds.getTotalCount();
28785         return {
28786             total : total,
28787             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28788             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28789         };
28790     },
28791
28792     // private
28793     onLoadError : function(){
28794         this.loading.enable();
28795     },
28796
28797     // private
28798     onPagingKeydown : function(e){
28799         var k = e.getKey();
28800         var d = this.getPageData();
28801         if(k == e.RETURN){
28802             var v = this.field.dom.value, pageNum;
28803             if(!v || isNaN(pageNum = parseInt(v, 10))){
28804                 this.field.dom.value = d.activePage;
28805                 return;
28806             }
28807             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28808             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28809             e.stopEvent();
28810         }
28811         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))
28812         {
28813           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28814           this.field.dom.value = pageNum;
28815           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28816           e.stopEvent();
28817         }
28818         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28819         {
28820           var v = this.field.dom.value, pageNum; 
28821           var increment = (e.shiftKey) ? 10 : 1;
28822           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28823             increment *= -1;
28824           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28825             this.field.dom.value = d.activePage;
28826             return;
28827           }
28828           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28829           {
28830             this.field.dom.value = parseInt(v, 10) + increment;
28831             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28832             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28833           }
28834           e.stopEvent();
28835         }
28836     },
28837
28838     // private
28839     beforeLoad : function(){
28840         if(this.loading){
28841             this.loading.disable();
28842         }
28843     },
28844
28845     // private
28846     onClick : function(which){
28847         var ds = this.ds;
28848         switch(which){
28849             case "first":
28850                 ds.load({params:{start: 0, limit: this.pageSize}});
28851             break;
28852             case "prev":
28853                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28854             break;
28855             case "next":
28856                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28857             break;
28858             case "last":
28859                 var total = ds.getTotalCount();
28860                 var extra = total % this.pageSize;
28861                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28862                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28863             break;
28864             case "refresh":
28865                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28866             break;
28867         }
28868     },
28869
28870     /**
28871      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28872      * @param {Roo.data.Store} store The data store to unbind
28873      */
28874     unbind : function(ds){
28875         ds.un("beforeload", this.beforeLoad, this);
28876         ds.un("load", this.onLoad, this);
28877         ds.un("loadexception", this.onLoadError, this);
28878         ds.un("remove", this.updateInfo, this);
28879         ds.un("add", this.updateInfo, this);
28880         this.ds = undefined;
28881     },
28882
28883     /**
28884      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28885      * @param {Roo.data.Store} store The data store to bind
28886      */
28887     bind : function(ds){
28888         ds.on("beforeload", this.beforeLoad, this);
28889         ds.on("load", this.onLoad, this);
28890         ds.on("loadexception", this.onLoadError, this);
28891         ds.on("remove", this.updateInfo, this);
28892         ds.on("add", this.updateInfo, this);
28893         this.ds = ds;
28894     }
28895 });/*
28896  * Based on:
28897  * Ext JS Library 1.1.1
28898  * Copyright(c) 2006-2007, Ext JS, LLC.
28899  *
28900  * Originally Released Under LGPL - original licence link has changed is not relivant.
28901  *
28902  * Fork - LGPL
28903  * <script type="text/javascript">
28904  */
28905
28906 /**
28907  * @class Roo.Resizable
28908  * @extends Roo.util.Observable
28909  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28910  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28911  * 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
28912  * the element will be wrapped for you automatically.</p>
28913  * <p>Here is the list of valid resize handles:</p>
28914  * <pre>
28915 Value   Description
28916 ------  -------------------
28917  'n'     north
28918  's'     south
28919  'e'     east
28920  'w'     west
28921  'nw'    northwest
28922  'sw'    southwest
28923  'se'    southeast
28924  'ne'    northeast
28925  'hd'    horizontal drag
28926  'all'   all
28927 </pre>
28928  * <p>Here's an example showing the creation of a typical Resizable:</p>
28929  * <pre><code>
28930 var resizer = new Roo.Resizable("element-id", {
28931     handles: 'all',
28932     minWidth: 200,
28933     minHeight: 100,
28934     maxWidth: 500,
28935     maxHeight: 400,
28936     pinned: true
28937 });
28938 resizer.on("resize", myHandler);
28939 </code></pre>
28940  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28941  * resizer.east.setDisplayed(false);</p>
28942  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28943  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28944  * resize operation's new size (defaults to [0, 0])
28945  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28946  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28947  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28948  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28949  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28950  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28951  * @cfg {Number} width The width of the element in pixels (defaults to null)
28952  * @cfg {Number} height The height of the element in pixels (defaults to null)
28953  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28954  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28955  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28956  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28957  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28958  * in favor of the handles config option (defaults to false)
28959  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28960  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28961  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28962  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28963  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28964  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28965  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28966  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28967  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28968  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28969  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28970  * @constructor
28971  * Create a new resizable component
28972  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28973  * @param {Object} config configuration options
28974   */
28975 Roo.Resizable = function(el, config)
28976 {
28977     this.el = Roo.get(el);
28978
28979     if(config && config.wrap){
28980         config.resizeChild = this.el;
28981         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28982         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28983         this.el.setStyle("overflow", "hidden");
28984         this.el.setPositioning(config.resizeChild.getPositioning());
28985         config.resizeChild.clearPositioning();
28986         if(!config.width || !config.height){
28987             var csize = config.resizeChild.getSize();
28988             this.el.setSize(csize.width, csize.height);
28989         }
28990         if(config.pinned && !config.adjustments){
28991             config.adjustments = "auto";
28992         }
28993     }
28994
28995     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28996     this.proxy.unselectable();
28997     this.proxy.enableDisplayMode('block');
28998
28999     Roo.apply(this, config);
29000
29001     if(this.pinned){
29002         this.disableTrackOver = true;
29003         this.el.addClass("x-resizable-pinned");
29004     }
29005     // if the element isn't positioned, make it relative
29006     var position = this.el.getStyle("position");
29007     if(position != "absolute" && position != "fixed"){
29008         this.el.setStyle("position", "relative");
29009     }
29010     if(!this.handles){ // no handles passed, must be legacy style
29011         this.handles = 's,e,se';
29012         if(this.multiDirectional){
29013             this.handles += ',n,w';
29014         }
29015     }
29016     if(this.handles == "all"){
29017         this.handles = "n s e w ne nw se sw";
29018     }
29019     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29020     var ps = Roo.Resizable.positions;
29021     for(var i = 0, len = hs.length; i < len; i++){
29022         if(hs[i] && ps[hs[i]]){
29023             var pos = ps[hs[i]];
29024             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29025         }
29026     }
29027     // legacy
29028     this.corner = this.southeast;
29029     
29030     // updateBox = the box can move..
29031     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29032         this.updateBox = true;
29033     }
29034
29035     this.activeHandle = null;
29036
29037     if(this.resizeChild){
29038         if(typeof this.resizeChild == "boolean"){
29039             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29040         }else{
29041             this.resizeChild = Roo.get(this.resizeChild, true);
29042         }
29043     }
29044     
29045     if(this.adjustments == "auto"){
29046         var rc = this.resizeChild;
29047         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29048         if(rc && (hw || hn)){
29049             rc.position("relative");
29050             rc.setLeft(hw ? hw.el.getWidth() : 0);
29051             rc.setTop(hn ? hn.el.getHeight() : 0);
29052         }
29053         this.adjustments = [
29054             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29055             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29056         ];
29057     }
29058
29059     if(this.draggable){
29060         this.dd = this.dynamic ?
29061             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29062         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29063     }
29064
29065     // public events
29066     this.addEvents({
29067         /**
29068          * @event beforeresize
29069          * Fired before resize is allowed. Set enabled to false to cancel resize.
29070          * @param {Roo.Resizable} this
29071          * @param {Roo.EventObject} e The mousedown event
29072          */
29073         "beforeresize" : true,
29074         /**
29075          * @event resizing
29076          * Fired a resizing.
29077          * @param {Roo.Resizable} this
29078          * @param {Number} x The new x position
29079          * @param {Number} y The new y position
29080          * @param {Number} w The new w width
29081          * @param {Number} h The new h hight
29082          * @param {Roo.EventObject} e The mouseup event
29083          */
29084         "resizing" : true,
29085         /**
29086          * @event resize
29087          * Fired after a resize.
29088          * @param {Roo.Resizable} this
29089          * @param {Number} width The new width
29090          * @param {Number} height The new height
29091          * @param {Roo.EventObject} e The mouseup event
29092          */
29093         "resize" : true
29094     });
29095
29096     if(this.width !== null && this.height !== null){
29097         this.resizeTo(this.width, this.height);
29098     }else{
29099         this.updateChildSize();
29100     }
29101     if(Roo.isIE){
29102         this.el.dom.style.zoom = 1;
29103     }
29104     Roo.Resizable.superclass.constructor.call(this);
29105 };
29106
29107 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29108         resizeChild : false,
29109         adjustments : [0, 0],
29110         minWidth : 5,
29111         minHeight : 5,
29112         maxWidth : 10000,
29113         maxHeight : 10000,
29114         enabled : true,
29115         animate : false,
29116         duration : .35,
29117         dynamic : false,
29118         handles : false,
29119         multiDirectional : false,
29120         disableTrackOver : false,
29121         easing : 'easeOutStrong',
29122         widthIncrement : 0,
29123         heightIncrement : 0,
29124         pinned : false,
29125         width : null,
29126         height : null,
29127         preserveRatio : false,
29128         transparent: false,
29129         minX: 0,
29130         minY: 0,
29131         draggable: false,
29132
29133         /**
29134          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29135          */
29136         constrainTo: undefined,
29137         /**
29138          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29139          */
29140         resizeRegion: undefined,
29141
29142
29143     /**
29144      * Perform a manual resize
29145      * @param {Number} width
29146      * @param {Number} height
29147      */
29148     resizeTo : function(width, height){
29149         this.el.setSize(width, height);
29150         this.updateChildSize();
29151         this.fireEvent("resize", this, width, height, null);
29152     },
29153
29154     // private
29155     startSizing : function(e, handle){
29156         this.fireEvent("beforeresize", this, e);
29157         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29158
29159             if(!this.overlay){
29160                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29161                 this.overlay.unselectable();
29162                 this.overlay.enableDisplayMode("block");
29163                 this.overlay.on("mousemove", this.onMouseMove, this);
29164                 this.overlay.on("mouseup", this.onMouseUp, this);
29165             }
29166             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29167
29168             this.resizing = true;
29169             this.startBox = this.el.getBox();
29170             this.startPoint = e.getXY();
29171             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29172                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29173
29174             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29175             this.overlay.show();
29176
29177             if(this.constrainTo) {
29178                 var ct = Roo.get(this.constrainTo);
29179                 this.resizeRegion = ct.getRegion().adjust(
29180                     ct.getFrameWidth('t'),
29181                     ct.getFrameWidth('l'),
29182                     -ct.getFrameWidth('b'),
29183                     -ct.getFrameWidth('r')
29184                 );
29185             }
29186
29187             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29188             this.proxy.show();
29189             this.proxy.setBox(this.startBox);
29190             if(!this.dynamic){
29191                 this.proxy.setStyle('visibility', 'visible');
29192             }
29193         }
29194     },
29195
29196     // private
29197     onMouseDown : function(handle, e){
29198         if(this.enabled){
29199             e.stopEvent();
29200             this.activeHandle = handle;
29201             this.startSizing(e, handle);
29202         }
29203     },
29204
29205     // private
29206     onMouseUp : function(e){
29207         var size = this.resizeElement();
29208         this.resizing = false;
29209         this.handleOut();
29210         this.overlay.hide();
29211         this.proxy.hide();
29212         this.fireEvent("resize", this, size.width, size.height, e);
29213     },
29214
29215     // private
29216     updateChildSize : function(){
29217         
29218         if(this.resizeChild){
29219             var el = this.el;
29220             var child = this.resizeChild;
29221             var adj = this.adjustments;
29222             if(el.dom.offsetWidth){
29223                 var b = el.getSize(true);
29224                 child.setSize(b.width+adj[0], b.height+adj[1]);
29225             }
29226             // Second call here for IE
29227             // The first call enables instant resizing and
29228             // the second call corrects scroll bars if they
29229             // exist
29230             if(Roo.isIE){
29231                 setTimeout(function(){
29232                     if(el.dom.offsetWidth){
29233                         var b = el.getSize(true);
29234                         child.setSize(b.width+adj[0], b.height+adj[1]);
29235                     }
29236                 }, 10);
29237             }
29238         }
29239     },
29240
29241     // private
29242     snap : function(value, inc, min){
29243         if(!inc || !value) return value;
29244         var newValue = value;
29245         var m = value % inc;
29246         if(m > 0){
29247             if(m > (inc/2)){
29248                 newValue = value + (inc-m);
29249             }else{
29250                 newValue = value - m;
29251             }
29252         }
29253         return Math.max(min, newValue);
29254     },
29255
29256     // private
29257     resizeElement : function(){
29258         var box = this.proxy.getBox();
29259         if(this.updateBox){
29260             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29261         }else{
29262             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29263         }
29264         this.updateChildSize();
29265         if(!this.dynamic){
29266             this.proxy.hide();
29267         }
29268         return box;
29269     },
29270
29271     // private
29272     constrain : function(v, diff, m, mx){
29273         if(v - diff < m){
29274             diff = v - m;
29275         }else if(v - diff > mx){
29276             diff = mx - v;
29277         }
29278         return diff;
29279     },
29280
29281     // private
29282     onMouseMove : function(e){
29283         
29284         if(this.enabled){
29285             try{// try catch so if something goes wrong the user doesn't get hung
29286
29287             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29288                 return;
29289             }
29290
29291             //var curXY = this.startPoint;
29292             var curSize = this.curSize || this.startBox;
29293             var x = this.startBox.x, y = this.startBox.y;
29294             var ox = x, oy = y;
29295             var w = curSize.width, h = curSize.height;
29296             var ow = w, oh = h;
29297             var mw = this.minWidth, mh = this.minHeight;
29298             var mxw = this.maxWidth, mxh = this.maxHeight;
29299             var wi = this.widthIncrement;
29300             var hi = this.heightIncrement;
29301
29302             var eventXY = e.getXY();
29303             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29304             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29305
29306             var pos = this.activeHandle.position;
29307
29308             switch(pos){
29309                 case "east":
29310                     w += diffX;
29311                     w = Math.min(Math.max(mw, w), mxw);
29312                     break;
29313              
29314                 case "south":
29315                     h += diffY;
29316                     h = Math.min(Math.max(mh, h), mxh);
29317                     break;
29318                 case "southeast":
29319                     w += diffX;
29320                     h += diffY;
29321                     w = Math.min(Math.max(mw, w), mxw);
29322                     h = Math.min(Math.max(mh, h), mxh);
29323                     break;
29324                 case "north":
29325                     diffY = this.constrain(h, diffY, mh, mxh);
29326                     y += diffY;
29327                     h -= diffY;
29328                     break;
29329                 case "hdrag":
29330                     
29331                     if (wi) {
29332                         var adiffX = Math.abs(diffX);
29333                         var sub = (adiffX % wi); // how much 
29334                         if (sub > (wi/2)) { // far enough to snap
29335                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29336                         } else {
29337                             // remove difference.. 
29338                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29339                         }
29340                     }
29341                     x += diffX;
29342                     x = Math.max(this.minX, x);
29343                     break;
29344                 case "west":
29345                     diffX = this.constrain(w, diffX, mw, mxw);
29346                     x += diffX;
29347                     w -= diffX;
29348                     break;
29349                 case "northeast":
29350                     w += diffX;
29351                     w = Math.min(Math.max(mw, w), mxw);
29352                     diffY = this.constrain(h, diffY, mh, mxh);
29353                     y += diffY;
29354                     h -= diffY;
29355                     break;
29356                 case "northwest":
29357                     diffX = this.constrain(w, diffX, mw, mxw);
29358                     diffY = this.constrain(h, diffY, mh, mxh);
29359                     y += diffY;
29360                     h -= diffY;
29361                     x += diffX;
29362                     w -= diffX;
29363                     break;
29364                case "southwest":
29365                     diffX = this.constrain(w, diffX, mw, mxw);
29366                     h += diffY;
29367                     h = Math.min(Math.max(mh, h), mxh);
29368                     x += diffX;
29369                     w -= diffX;
29370                     break;
29371             }
29372
29373             var sw = this.snap(w, wi, mw);
29374             var sh = this.snap(h, hi, mh);
29375             if(sw != w || sh != h){
29376                 switch(pos){
29377                     case "northeast":
29378                         y -= sh - h;
29379                     break;
29380                     case "north":
29381                         y -= sh - h;
29382                         break;
29383                     case "southwest":
29384                         x -= sw - w;
29385                     break;
29386                     case "west":
29387                         x -= sw - w;
29388                         break;
29389                     case "northwest":
29390                         x -= sw - w;
29391                         y -= sh - h;
29392                     break;
29393                 }
29394                 w = sw;
29395                 h = sh;
29396             }
29397
29398             if(this.preserveRatio){
29399                 switch(pos){
29400                     case "southeast":
29401                     case "east":
29402                         h = oh * (w/ow);
29403                         h = Math.min(Math.max(mh, h), mxh);
29404                         w = ow * (h/oh);
29405                        break;
29406                     case "south":
29407                         w = ow * (h/oh);
29408                         w = Math.min(Math.max(mw, w), mxw);
29409                         h = oh * (w/ow);
29410                         break;
29411                     case "northeast":
29412                         w = ow * (h/oh);
29413                         w = Math.min(Math.max(mw, w), mxw);
29414                         h = oh * (w/ow);
29415                     break;
29416                     case "north":
29417                         var tw = w;
29418                         w = ow * (h/oh);
29419                         w = Math.min(Math.max(mw, w), mxw);
29420                         h = oh * (w/ow);
29421                         x += (tw - w) / 2;
29422                         break;
29423                     case "southwest":
29424                         h = oh * (w/ow);
29425                         h = Math.min(Math.max(mh, h), mxh);
29426                         var tw = w;
29427                         w = ow * (h/oh);
29428                         x += tw - w;
29429                         break;
29430                     case "west":
29431                         var th = h;
29432                         h = oh * (w/ow);
29433                         h = Math.min(Math.max(mh, h), mxh);
29434                         y += (th - h) / 2;
29435                         var tw = w;
29436                         w = ow * (h/oh);
29437                         x += tw - w;
29438                        break;
29439                     case "northwest":
29440                         var tw = w;
29441                         var th = h;
29442                         h = oh * (w/ow);
29443                         h = Math.min(Math.max(mh, h), mxh);
29444                         w = ow * (h/oh);
29445                         y += th - h;
29446                         x += tw - w;
29447                        break;
29448
29449                 }
29450             }
29451             if (pos == 'hdrag') {
29452                 w = ow;
29453             }
29454             this.proxy.setBounds(x, y, w, h);
29455             if(this.dynamic){
29456                 this.resizeElement();
29457             }
29458             }catch(e){}
29459         }
29460         this.fireEvent("resizing", this, x, y, w, h, e);
29461     },
29462
29463     // private
29464     handleOver : function(){
29465         if(this.enabled){
29466             this.el.addClass("x-resizable-over");
29467         }
29468     },
29469
29470     // private
29471     handleOut : function(){
29472         if(!this.resizing){
29473             this.el.removeClass("x-resizable-over");
29474         }
29475     },
29476
29477     /**
29478      * Returns the element this component is bound to.
29479      * @return {Roo.Element}
29480      */
29481     getEl : function(){
29482         return this.el;
29483     },
29484
29485     /**
29486      * Returns the resizeChild element (or null).
29487      * @return {Roo.Element}
29488      */
29489     getResizeChild : function(){
29490         return this.resizeChild;
29491     },
29492     groupHandler : function()
29493     {
29494         
29495     },
29496     /**
29497      * Destroys this resizable. If the element was wrapped and
29498      * removeEl is not true then the element remains.
29499      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29500      */
29501     destroy : function(removeEl){
29502         this.proxy.remove();
29503         if(this.overlay){
29504             this.overlay.removeAllListeners();
29505             this.overlay.remove();
29506         }
29507         var ps = Roo.Resizable.positions;
29508         for(var k in ps){
29509             if(typeof ps[k] != "function" && this[ps[k]]){
29510                 var h = this[ps[k]];
29511                 h.el.removeAllListeners();
29512                 h.el.remove();
29513             }
29514         }
29515         if(removeEl){
29516             this.el.update("");
29517             this.el.remove();
29518         }
29519     }
29520 });
29521
29522 // private
29523 // hash to map config positions to true positions
29524 Roo.Resizable.positions = {
29525     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29526     hd: "hdrag"
29527 };
29528
29529 // private
29530 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29531     if(!this.tpl){
29532         // only initialize the template if resizable is used
29533         var tpl = Roo.DomHelper.createTemplate(
29534             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29535         );
29536         tpl.compile();
29537         Roo.Resizable.Handle.prototype.tpl = tpl;
29538     }
29539     this.position = pos;
29540     this.rz = rz;
29541     // show north drag fro topdra
29542     var handlepos = pos == 'hdrag' ? 'north' : pos;
29543     
29544     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29545     if (pos == 'hdrag') {
29546         this.el.setStyle('cursor', 'pointer');
29547     }
29548     this.el.unselectable();
29549     if(transparent){
29550         this.el.setOpacity(0);
29551     }
29552     this.el.on("mousedown", this.onMouseDown, this);
29553     if(!disableTrackOver){
29554         this.el.on("mouseover", this.onMouseOver, this);
29555         this.el.on("mouseout", this.onMouseOut, this);
29556     }
29557 };
29558
29559 // private
29560 Roo.Resizable.Handle.prototype = {
29561     afterResize : function(rz){
29562         // do nothing
29563     },
29564     // private
29565     onMouseDown : function(e){
29566         this.rz.onMouseDown(this, e);
29567     },
29568     // private
29569     onMouseOver : function(e){
29570         this.rz.handleOver(this, e);
29571     },
29572     // private
29573     onMouseOut : function(e){
29574         this.rz.handleOut(this, e);
29575     }
29576 };/*
29577  * Based on:
29578  * Ext JS Library 1.1.1
29579  * Copyright(c) 2006-2007, Ext JS, LLC.
29580  *
29581  * Originally Released Under LGPL - original licence link has changed is not relivant.
29582  *
29583  * Fork - LGPL
29584  * <script type="text/javascript">
29585  */
29586
29587 /**
29588  * @class Roo.Editor
29589  * @extends Roo.Component
29590  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29591  * @constructor
29592  * Create a new Editor
29593  * @param {Roo.form.Field} field The Field object (or descendant)
29594  * @param {Object} config The config object
29595  */
29596 Roo.Editor = function(field, config){
29597     Roo.Editor.superclass.constructor.call(this, config);
29598     this.field = field;
29599     this.addEvents({
29600         /**
29601              * @event beforestartedit
29602              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29603              * false from the handler of this event.
29604              * @param {Editor} this
29605              * @param {Roo.Element} boundEl The underlying element bound to this editor
29606              * @param {Mixed} value The field value being set
29607              */
29608         "beforestartedit" : true,
29609         /**
29610              * @event startedit
29611              * Fires when this editor is displayed
29612              * @param {Roo.Element} boundEl The underlying element bound to this editor
29613              * @param {Mixed} value The starting field value
29614              */
29615         "startedit" : true,
29616         /**
29617              * @event beforecomplete
29618              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29619              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29620              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29621              * event will not fire since no edit actually occurred.
29622              * @param {Editor} this
29623              * @param {Mixed} value The current field value
29624              * @param {Mixed} startValue The original field value
29625              */
29626         "beforecomplete" : true,
29627         /**
29628              * @event complete
29629              * Fires after editing is complete and any changed value has been written to the underlying field.
29630              * @param {Editor} this
29631              * @param {Mixed} value The current field value
29632              * @param {Mixed} startValue The original field value
29633              */
29634         "complete" : true,
29635         /**
29636          * @event specialkey
29637          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29638          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29639          * @param {Roo.form.Field} this
29640          * @param {Roo.EventObject} e The event object
29641          */
29642         "specialkey" : true
29643     });
29644 };
29645
29646 Roo.extend(Roo.Editor, Roo.Component, {
29647     /**
29648      * @cfg {Boolean/String} autosize
29649      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29650      * or "height" to adopt the height only (defaults to false)
29651      */
29652     /**
29653      * @cfg {Boolean} revertInvalid
29654      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29655      * validation fails (defaults to true)
29656      */
29657     /**
29658      * @cfg {Boolean} ignoreNoChange
29659      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29660      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29661      * will never be ignored.
29662      */
29663     /**
29664      * @cfg {Boolean} hideEl
29665      * False to keep the bound element visible while the editor is displayed (defaults to true)
29666      */
29667     /**
29668      * @cfg {Mixed} value
29669      * The data value of the underlying field (defaults to "")
29670      */
29671     value : "",
29672     /**
29673      * @cfg {String} alignment
29674      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29675      */
29676     alignment: "c-c?",
29677     /**
29678      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29679      * for bottom-right shadow (defaults to "frame")
29680      */
29681     shadow : "frame",
29682     /**
29683      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29684      */
29685     constrain : false,
29686     /**
29687      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29688      */
29689     completeOnEnter : false,
29690     /**
29691      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29692      */
29693     cancelOnEsc : false,
29694     /**
29695      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29696      */
29697     updateEl : false,
29698
29699     // private
29700     onRender : function(ct, position){
29701         this.el = new Roo.Layer({
29702             shadow: this.shadow,
29703             cls: "x-editor",
29704             parentEl : ct,
29705             shim : this.shim,
29706             shadowOffset:4,
29707             id: this.id,
29708             constrain: this.constrain
29709         });
29710         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29711         if(this.field.msgTarget != 'title'){
29712             this.field.msgTarget = 'qtip';
29713         }
29714         this.field.render(this.el);
29715         if(Roo.isGecko){
29716             this.field.el.dom.setAttribute('autocomplete', 'off');
29717         }
29718         this.field.on("specialkey", this.onSpecialKey, this);
29719         if(this.swallowKeys){
29720             this.field.el.swallowEvent(['keydown','keypress']);
29721         }
29722         this.field.show();
29723         this.field.on("blur", this.onBlur, this);
29724         if(this.field.grow){
29725             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29726         }
29727     },
29728
29729     onSpecialKey : function(field, e)
29730     {
29731         //Roo.log('editor onSpecialKey');
29732         if(this.completeOnEnter && e.getKey() == e.ENTER){
29733             e.stopEvent();
29734             this.completeEdit();
29735             return;
29736         }
29737         // do not fire special key otherwise it might hide close the editor...
29738         if(e.getKey() == e.ENTER){    
29739             return;
29740         }
29741         if(this.cancelOnEsc && e.getKey() == e.ESC){
29742             this.cancelEdit();
29743             return;
29744         } 
29745         this.fireEvent('specialkey', field, e);
29746     
29747     },
29748
29749     /**
29750      * Starts the editing process and shows the editor.
29751      * @param {String/HTMLElement/Element} el The element to edit
29752      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29753       * to the innerHTML of el.
29754      */
29755     startEdit : function(el, value){
29756         if(this.editing){
29757             this.completeEdit();
29758         }
29759         this.boundEl = Roo.get(el);
29760         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29761         if(!this.rendered){
29762             this.render(this.parentEl || document.body);
29763         }
29764         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29765             return;
29766         }
29767         this.startValue = v;
29768         this.field.setValue(v);
29769         if(this.autoSize){
29770             var sz = this.boundEl.getSize();
29771             switch(this.autoSize){
29772                 case "width":
29773                 this.setSize(sz.width,  "");
29774                 break;
29775                 case "height":
29776                 this.setSize("",  sz.height);
29777                 break;
29778                 default:
29779                 this.setSize(sz.width,  sz.height);
29780             }
29781         }
29782         this.el.alignTo(this.boundEl, this.alignment);
29783         this.editing = true;
29784         if(Roo.QuickTips){
29785             Roo.QuickTips.disable();
29786         }
29787         this.show();
29788     },
29789
29790     /**
29791      * Sets the height and width of this editor.
29792      * @param {Number} width The new width
29793      * @param {Number} height The new height
29794      */
29795     setSize : function(w, h){
29796         this.field.setSize(w, h);
29797         if(this.el){
29798             this.el.sync();
29799         }
29800     },
29801
29802     /**
29803      * Realigns the editor to the bound field based on the current alignment config value.
29804      */
29805     realign : function(){
29806         this.el.alignTo(this.boundEl, this.alignment);
29807     },
29808
29809     /**
29810      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29811      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29812      */
29813     completeEdit : function(remainVisible){
29814         if(!this.editing){
29815             return;
29816         }
29817         var v = this.getValue();
29818         if(this.revertInvalid !== false && !this.field.isValid()){
29819             v = this.startValue;
29820             this.cancelEdit(true);
29821         }
29822         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29823             this.editing = false;
29824             this.hide();
29825             return;
29826         }
29827         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29828             this.editing = false;
29829             if(this.updateEl && this.boundEl){
29830                 this.boundEl.update(v);
29831             }
29832             if(remainVisible !== true){
29833                 this.hide();
29834             }
29835             this.fireEvent("complete", this, v, this.startValue);
29836         }
29837     },
29838
29839     // private
29840     onShow : function(){
29841         this.el.show();
29842         if(this.hideEl !== false){
29843             this.boundEl.hide();
29844         }
29845         this.field.show();
29846         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29847             this.fixIEFocus = true;
29848             this.deferredFocus.defer(50, this);
29849         }else{
29850             this.field.focus();
29851         }
29852         this.fireEvent("startedit", this.boundEl, this.startValue);
29853     },
29854
29855     deferredFocus : function(){
29856         if(this.editing){
29857             this.field.focus();
29858         }
29859     },
29860
29861     /**
29862      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29863      * reverted to the original starting value.
29864      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29865      * cancel (defaults to false)
29866      */
29867     cancelEdit : function(remainVisible){
29868         if(this.editing){
29869             this.setValue(this.startValue);
29870             if(remainVisible !== true){
29871                 this.hide();
29872             }
29873         }
29874     },
29875
29876     // private
29877     onBlur : function(){
29878         if(this.allowBlur !== true && this.editing){
29879             this.completeEdit();
29880         }
29881     },
29882
29883     // private
29884     onHide : function(){
29885         if(this.editing){
29886             this.completeEdit();
29887             return;
29888         }
29889         this.field.blur();
29890         if(this.field.collapse){
29891             this.field.collapse();
29892         }
29893         this.el.hide();
29894         if(this.hideEl !== false){
29895             this.boundEl.show();
29896         }
29897         if(Roo.QuickTips){
29898             Roo.QuickTips.enable();
29899         }
29900     },
29901
29902     /**
29903      * Sets the data value of the editor
29904      * @param {Mixed} value Any valid value supported by the underlying field
29905      */
29906     setValue : function(v){
29907         this.field.setValue(v);
29908     },
29909
29910     /**
29911      * Gets the data value of the editor
29912      * @return {Mixed} The data value
29913      */
29914     getValue : function(){
29915         return this.field.getValue();
29916     }
29917 });/*
29918  * Based on:
29919  * Ext JS Library 1.1.1
29920  * Copyright(c) 2006-2007, Ext JS, LLC.
29921  *
29922  * Originally Released Under LGPL - original licence link has changed is not relivant.
29923  *
29924  * Fork - LGPL
29925  * <script type="text/javascript">
29926  */
29927  
29928 /**
29929  * @class Roo.BasicDialog
29930  * @extends Roo.util.Observable
29931  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29932  * <pre><code>
29933 var dlg = new Roo.BasicDialog("my-dlg", {
29934     height: 200,
29935     width: 300,
29936     minHeight: 100,
29937     minWidth: 150,
29938     modal: true,
29939     proxyDrag: true,
29940     shadow: true
29941 });
29942 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29943 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29944 dlg.addButton('Cancel', dlg.hide, dlg);
29945 dlg.show();
29946 </code></pre>
29947   <b>A Dialog should always be a direct child of the body element.</b>
29948  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29949  * @cfg {String} title Default text to display in the title bar (defaults to null)
29950  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29951  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29952  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29953  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29954  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29955  * (defaults to null with no animation)
29956  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29957  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29958  * property for valid values (defaults to 'all')
29959  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29960  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29961  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29962  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29963  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29964  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29965  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29966  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29967  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29968  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29969  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29970  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29971  * draggable = true (defaults to false)
29972  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29973  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29974  * shadow (defaults to false)
29975  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29976  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29977  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29978  * @cfg {Array} buttons Array of buttons
29979  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29980  * @constructor
29981  * Create a new BasicDialog.
29982  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29983  * @param {Object} config Configuration options
29984  */
29985 Roo.BasicDialog = function(el, config){
29986     this.el = Roo.get(el);
29987     var dh = Roo.DomHelper;
29988     if(!this.el && config && config.autoCreate){
29989         if(typeof config.autoCreate == "object"){
29990             if(!config.autoCreate.id){
29991                 config.autoCreate.id = el;
29992             }
29993             this.el = dh.append(document.body,
29994                         config.autoCreate, true);
29995         }else{
29996             this.el = dh.append(document.body,
29997                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29998         }
29999     }
30000     el = this.el;
30001     el.setDisplayed(true);
30002     el.hide = this.hideAction;
30003     this.id = el.id;
30004     el.addClass("x-dlg");
30005
30006     Roo.apply(this, config);
30007
30008     this.proxy = el.createProxy("x-dlg-proxy");
30009     this.proxy.hide = this.hideAction;
30010     this.proxy.setOpacity(.5);
30011     this.proxy.hide();
30012
30013     if(config.width){
30014         el.setWidth(config.width);
30015     }
30016     if(config.height){
30017         el.setHeight(config.height);
30018     }
30019     this.size = el.getSize();
30020     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30021         this.xy = [config.x,config.y];
30022     }else{
30023         this.xy = el.getCenterXY(true);
30024     }
30025     /** The header element @type Roo.Element */
30026     this.header = el.child("> .x-dlg-hd");
30027     /** The body element @type Roo.Element */
30028     this.body = el.child("> .x-dlg-bd");
30029     /** The footer element @type Roo.Element */
30030     this.footer = el.child("> .x-dlg-ft");
30031
30032     if(!this.header){
30033         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30034     }
30035     if(!this.body){
30036         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30037     }
30038
30039     this.header.unselectable();
30040     if(this.title){
30041         this.header.update(this.title);
30042     }
30043     // this element allows the dialog to be focused for keyboard event
30044     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30045     this.focusEl.swallowEvent("click", true);
30046
30047     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30048
30049     // wrap the body and footer for special rendering
30050     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30051     if(this.footer){
30052         this.bwrap.dom.appendChild(this.footer.dom);
30053     }
30054
30055     this.bg = this.el.createChild({
30056         tag: "div", cls:"x-dlg-bg",
30057         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30058     });
30059     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30060
30061
30062     if(this.autoScroll !== false && !this.autoTabs){
30063         this.body.setStyle("overflow", "auto");
30064     }
30065
30066     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30067
30068     if(this.closable !== false){
30069         this.el.addClass("x-dlg-closable");
30070         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30071         this.close.on("click", this.closeClick, this);
30072         this.close.addClassOnOver("x-dlg-close-over");
30073     }
30074     if(this.collapsible !== false){
30075         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30076         this.collapseBtn.on("click", this.collapseClick, this);
30077         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30078         this.header.on("dblclick", this.collapseClick, this);
30079     }
30080     if(this.resizable !== false){
30081         this.el.addClass("x-dlg-resizable");
30082         this.resizer = new Roo.Resizable(el, {
30083             minWidth: this.minWidth || 80,
30084             minHeight:this.minHeight || 80,
30085             handles: this.resizeHandles || "all",
30086             pinned: true
30087         });
30088         this.resizer.on("beforeresize", this.beforeResize, this);
30089         this.resizer.on("resize", this.onResize, this);
30090     }
30091     if(this.draggable !== false){
30092         el.addClass("x-dlg-draggable");
30093         if (!this.proxyDrag) {
30094             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30095         }
30096         else {
30097             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30098         }
30099         dd.setHandleElId(this.header.id);
30100         dd.endDrag = this.endMove.createDelegate(this);
30101         dd.startDrag = this.startMove.createDelegate(this);
30102         dd.onDrag = this.onDrag.createDelegate(this);
30103         dd.scroll = false;
30104         this.dd = dd;
30105     }
30106     if(this.modal){
30107         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30108         this.mask.enableDisplayMode("block");
30109         this.mask.hide();
30110         this.el.addClass("x-dlg-modal");
30111     }
30112     if(this.shadow){
30113         this.shadow = new Roo.Shadow({
30114             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30115             offset : this.shadowOffset
30116         });
30117     }else{
30118         this.shadowOffset = 0;
30119     }
30120     if(Roo.useShims && this.shim !== false){
30121         this.shim = this.el.createShim();
30122         this.shim.hide = this.hideAction;
30123         this.shim.hide();
30124     }else{
30125         this.shim = false;
30126     }
30127     if(this.autoTabs){
30128         this.initTabs();
30129     }
30130     if (this.buttons) { 
30131         var bts= this.buttons;
30132         this.buttons = [];
30133         Roo.each(bts, function(b) {
30134             this.addButton(b);
30135         }, this);
30136     }
30137     
30138     
30139     this.addEvents({
30140         /**
30141          * @event keydown
30142          * Fires when a key is pressed
30143          * @param {Roo.BasicDialog} this
30144          * @param {Roo.EventObject} e
30145          */
30146         "keydown" : true,
30147         /**
30148          * @event move
30149          * Fires when this dialog is moved by the user.
30150          * @param {Roo.BasicDialog} this
30151          * @param {Number} x The new page X
30152          * @param {Number} y The new page Y
30153          */
30154         "move" : true,
30155         /**
30156          * @event resize
30157          * Fires when this dialog is resized by the user.
30158          * @param {Roo.BasicDialog} this
30159          * @param {Number} width The new width
30160          * @param {Number} height The new height
30161          */
30162         "resize" : true,
30163         /**
30164          * @event beforehide
30165          * Fires before this dialog is hidden.
30166          * @param {Roo.BasicDialog} this
30167          */
30168         "beforehide" : true,
30169         /**
30170          * @event hide
30171          * Fires when this dialog is hidden.
30172          * @param {Roo.BasicDialog} this
30173          */
30174         "hide" : true,
30175         /**
30176          * @event beforeshow
30177          * Fires before this dialog is shown.
30178          * @param {Roo.BasicDialog} this
30179          */
30180         "beforeshow" : true,
30181         /**
30182          * @event show
30183          * Fires when this dialog is shown.
30184          * @param {Roo.BasicDialog} this
30185          */
30186         "show" : true
30187     });
30188     el.on("keydown", this.onKeyDown, this);
30189     el.on("mousedown", this.toFront, this);
30190     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30191     this.el.hide();
30192     Roo.DialogManager.register(this);
30193     Roo.BasicDialog.superclass.constructor.call(this);
30194 };
30195
30196 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30197     shadowOffset: Roo.isIE ? 6 : 5,
30198     minHeight: 80,
30199     minWidth: 200,
30200     minButtonWidth: 75,
30201     defaultButton: null,
30202     buttonAlign: "right",
30203     tabTag: 'div',
30204     firstShow: true,
30205
30206     /**
30207      * Sets the dialog title text
30208      * @param {String} text The title text to display
30209      * @return {Roo.BasicDialog} this
30210      */
30211     setTitle : function(text){
30212         this.header.update(text);
30213         return this;
30214     },
30215
30216     // private
30217     closeClick : function(){
30218         this.hide();
30219     },
30220
30221     // private
30222     collapseClick : function(){
30223         this[this.collapsed ? "expand" : "collapse"]();
30224     },
30225
30226     /**
30227      * Collapses the dialog to its minimized state (only the title bar is visible).
30228      * Equivalent to the user clicking the collapse dialog button.
30229      */
30230     collapse : function(){
30231         if(!this.collapsed){
30232             this.collapsed = true;
30233             this.el.addClass("x-dlg-collapsed");
30234             this.restoreHeight = this.el.getHeight();
30235             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30236         }
30237     },
30238
30239     /**
30240      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30241      * clicking the expand dialog button.
30242      */
30243     expand : function(){
30244         if(this.collapsed){
30245             this.collapsed = false;
30246             this.el.removeClass("x-dlg-collapsed");
30247             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30248         }
30249     },
30250
30251     /**
30252      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30253      * @return {Roo.TabPanel} The tabs component
30254      */
30255     initTabs : function(){
30256         var tabs = this.getTabs();
30257         while(tabs.getTab(0)){
30258             tabs.removeTab(0);
30259         }
30260         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30261             var dom = el.dom;
30262             tabs.addTab(Roo.id(dom), dom.title);
30263             dom.title = "";
30264         });
30265         tabs.activate(0);
30266         return tabs;
30267     },
30268
30269     // private
30270     beforeResize : function(){
30271         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30272     },
30273
30274     // private
30275     onResize : function(){
30276         this.refreshSize();
30277         this.syncBodyHeight();
30278         this.adjustAssets();
30279         this.focus();
30280         this.fireEvent("resize", this, this.size.width, this.size.height);
30281     },
30282
30283     // private
30284     onKeyDown : function(e){
30285         if(this.isVisible()){
30286             this.fireEvent("keydown", this, e);
30287         }
30288     },
30289
30290     /**
30291      * Resizes the dialog.
30292      * @param {Number} width
30293      * @param {Number} height
30294      * @return {Roo.BasicDialog} this
30295      */
30296     resizeTo : function(width, height){
30297         this.el.setSize(width, height);
30298         this.size = {width: width, height: height};
30299         this.syncBodyHeight();
30300         if(this.fixedcenter){
30301             this.center();
30302         }
30303         if(this.isVisible()){
30304             this.constrainXY();
30305             this.adjustAssets();
30306         }
30307         this.fireEvent("resize", this, width, height);
30308         return this;
30309     },
30310
30311
30312     /**
30313      * Resizes the dialog to fit the specified content size.
30314      * @param {Number} width
30315      * @param {Number} height
30316      * @return {Roo.BasicDialog} this
30317      */
30318     setContentSize : function(w, h){
30319         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30320         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30321         //if(!this.el.isBorderBox()){
30322             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30323             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30324         //}
30325         if(this.tabs){
30326             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30327             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30328         }
30329         this.resizeTo(w, h);
30330         return this;
30331     },
30332
30333     /**
30334      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30335      * executed in response to a particular key being pressed while the dialog is active.
30336      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30337      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30338      * @param {Function} fn The function to call
30339      * @param {Object} scope (optional) The scope of the function
30340      * @return {Roo.BasicDialog} this
30341      */
30342     addKeyListener : function(key, fn, scope){
30343         var keyCode, shift, ctrl, alt;
30344         if(typeof key == "object" && !(key instanceof Array)){
30345             keyCode = key["key"];
30346             shift = key["shift"];
30347             ctrl = key["ctrl"];
30348             alt = key["alt"];
30349         }else{
30350             keyCode = key;
30351         }
30352         var handler = function(dlg, e){
30353             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30354                 var k = e.getKey();
30355                 if(keyCode instanceof Array){
30356                     for(var i = 0, len = keyCode.length; i < len; i++){
30357                         if(keyCode[i] == k){
30358                           fn.call(scope || window, dlg, k, e);
30359                           return;
30360                         }
30361                     }
30362                 }else{
30363                     if(k == keyCode){
30364                         fn.call(scope || window, dlg, k, e);
30365                     }
30366                 }
30367             }
30368         };
30369         this.on("keydown", handler);
30370         return this;
30371     },
30372
30373     /**
30374      * Returns the TabPanel component (creates it if it doesn't exist).
30375      * Note: If you wish to simply check for the existence of tabs without creating them,
30376      * check for a null 'tabs' property.
30377      * @return {Roo.TabPanel} The tabs component
30378      */
30379     getTabs : function(){
30380         if(!this.tabs){
30381             this.el.addClass("x-dlg-auto-tabs");
30382             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30383             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30384         }
30385         return this.tabs;
30386     },
30387
30388     /**
30389      * Adds a button to the footer section of the dialog.
30390      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30391      * object or a valid Roo.DomHelper element config
30392      * @param {Function} handler The function called when the button is clicked
30393      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30394      * @return {Roo.Button} The new button
30395      */
30396     addButton : function(config, handler, scope){
30397         var dh = Roo.DomHelper;
30398         if(!this.footer){
30399             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30400         }
30401         if(!this.btnContainer){
30402             var tb = this.footer.createChild({
30403
30404                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30405                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30406             }, null, true);
30407             this.btnContainer = tb.firstChild.firstChild.firstChild;
30408         }
30409         var bconfig = {
30410             handler: handler,
30411             scope: scope,
30412             minWidth: this.minButtonWidth,
30413             hideParent:true
30414         };
30415         if(typeof config == "string"){
30416             bconfig.text = config;
30417         }else{
30418             if(config.tag){
30419                 bconfig.dhconfig = config;
30420             }else{
30421                 Roo.apply(bconfig, config);
30422             }
30423         }
30424         var fc = false;
30425         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30426             bconfig.position = Math.max(0, bconfig.position);
30427             fc = this.btnContainer.childNodes[bconfig.position];
30428         }
30429          
30430         var btn = new Roo.Button(
30431             fc ? 
30432                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30433                 : this.btnContainer.appendChild(document.createElement("td")),
30434             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30435             bconfig
30436         );
30437         this.syncBodyHeight();
30438         if(!this.buttons){
30439             /**
30440              * Array of all the buttons that have been added to this dialog via addButton
30441              * @type Array
30442              */
30443             this.buttons = [];
30444         }
30445         this.buttons.push(btn);
30446         return btn;
30447     },
30448
30449     /**
30450      * Sets the default button to be focused when the dialog is displayed.
30451      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30452      * @return {Roo.BasicDialog} this
30453      */
30454     setDefaultButton : function(btn){
30455         this.defaultButton = btn;
30456         return this;
30457     },
30458
30459     // private
30460     getHeaderFooterHeight : function(safe){
30461         var height = 0;
30462         if(this.header){
30463            height += this.header.getHeight();
30464         }
30465         if(this.footer){
30466            var fm = this.footer.getMargins();
30467             height += (this.footer.getHeight()+fm.top+fm.bottom);
30468         }
30469         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30470         height += this.centerBg.getPadding("tb");
30471         return height;
30472     },
30473
30474     // private
30475     syncBodyHeight : function()
30476     {
30477         var bd = this.body, // the text
30478             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30479             bw = this.bwrap;
30480         var height = this.size.height - this.getHeaderFooterHeight(false);
30481         bd.setHeight(height-bd.getMargins("tb"));
30482         var hh = this.header.getHeight();
30483         var h = this.size.height-hh;
30484         cb.setHeight(h);
30485         
30486         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30487         bw.setHeight(h-cb.getPadding("tb"));
30488         
30489         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30490         bd.setWidth(bw.getWidth(true));
30491         if(this.tabs){
30492             this.tabs.syncHeight();
30493             if(Roo.isIE){
30494                 this.tabs.el.repaint();
30495             }
30496         }
30497     },
30498
30499     /**
30500      * Restores the previous state of the dialog if Roo.state is configured.
30501      * @return {Roo.BasicDialog} this
30502      */
30503     restoreState : function(){
30504         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30505         if(box && box.width){
30506             this.xy = [box.x, box.y];
30507             this.resizeTo(box.width, box.height);
30508         }
30509         return this;
30510     },
30511
30512     // private
30513     beforeShow : function(){
30514         this.expand();
30515         if(this.fixedcenter){
30516             this.xy = this.el.getCenterXY(true);
30517         }
30518         if(this.modal){
30519             Roo.get(document.body).addClass("x-body-masked");
30520             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30521             this.mask.show();
30522         }
30523         this.constrainXY();
30524     },
30525
30526     // private
30527     animShow : function(){
30528         var b = Roo.get(this.animateTarget).getBox();
30529         this.proxy.setSize(b.width, b.height);
30530         this.proxy.setLocation(b.x, b.y);
30531         this.proxy.show();
30532         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30533                     true, .35, this.showEl.createDelegate(this));
30534     },
30535
30536     /**
30537      * Shows the dialog.
30538      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30539      * @return {Roo.BasicDialog} this
30540      */
30541     show : function(animateTarget){
30542         if (this.fireEvent("beforeshow", this) === false){
30543             return;
30544         }
30545         if(this.syncHeightBeforeShow){
30546             this.syncBodyHeight();
30547         }else if(this.firstShow){
30548             this.firstShow = false;
30549             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30550         }
30551         this.animateTarget = animateTarget || this.animateTarget;
30552         if(!this.el.isVisible()){
30553             this.beforeShow();
30554             if(this.animateTarget && Roo.get(this.animateTarget)){
30555                 this.animShow();
30556             }else{
30557                 this.showEl();
30558             }
30559         }
30560         return this;
30561     },
30562
30563     // private
30564     showEl : function(){
30565         this.proxy.hide();
30566         this.el.setXY(this.xy);
30567         this.el.show();
30568         this.adjustAssets(true);
30569         this.toFront();
30570         this.focus();
30571         // IE peekaboo bug - fix found by Dave Fenwick
30572         if(Roo.isIE){
30573             this.el.repaint();
30574         }
30575         this.fireEvent("show", this);
30576     },
30577
30578     /**
30579      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30580      * dialog itself will receive focus.
30581      */
30582     focus : function(){
30583         if(this.defaultButton){
30584             this.defaultButton.focus();
30585         }else{
30586             this.focusEl.focus();
30587         }
30588     },
30589
30590     // private
30591     constrainXY : function(){
30592         if(this.constraintoviewport !== false){
30593             if(!this.viewSize){
30594                 if(this.container){
30595                     var s = this.container.getSize();
30596                     this.viewSize = [s.width, s.height];
30597                 }else{
30598                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30599                 }
30600             }
30601             var s = Roo.get(this.container||document).getScroll();
30602
30603             var x = this.xy[0], y = this.xy[1];
30604             var w = this.size.width, h = this.size.height;
30605             var vw = this.viewSize[0], vh = this.viewSize[1];
30606             // only move it if it needs it
30607             var moved = false;
30608             // first validate right/bottom
30609             if(x + w > vw+s.left){
30610                 x = vw - w;
30611                 moved = true;
30612             }
30613             if(y + h > vh+s.top){
30614                 y = vh - h;
30615                 moved = true;
30616             }
30617             // then make sure top/left isn't negative
30618             if(x < s.left){
30619                 x = s.left;
30620                 moved = true;
30621             }
30622             if(y < s.top){
30623                 y = s.top;
30624                 moved = true;
30625             }
30626             if(moved){
30627                 // cache xy
30628                 this.xy = [x, y];
30629                 if(this.isVisible()){
30630                     this.el.setLocation(x, y);
30631                     this.adjustAssets();
30632                 }
30633             }
30634         }
30635     },
30636
30637     // private
30638     onDrag : function(){
30639         if(!this.proxyDrag){
30640             this.xy = this.el.getXY();
30641             this.adjustAssets();
30642         }
30643     },
30644
30645     // private
30646     adjustAssets : function(doShow){
30647         var x = this.xy[0], y = this.xy[1];
30648         var w = this.size.width, h = this.size.height;
30649         if(doShow === true){
30650             if(this.shadow){
30651                 this.shadow.show(this.el);
30652             }
30653             if(this.shim){
30654                 this.shim.show();
30655             }
30656         }
30657         if(this.shadow && this.shadow.isVisible()){
30658             this.shadow.show(this.el);
30659         }
30660         if(this.shim && this.shim.isVisible()){
30661             this.shim.setBounds(x, y, w, h);
30662         }
30663     },
30664
30665     // private
30666     adjustViewport : function(w, h){
30667         if(!w || !h){
30668             w = Roo.lib.Dom.getViewWidth();
30669             h = Roo.lib.Dom.getViewHeight();
30670         }
30671         // cache the size
30672         this.viewSize = [w, h];
30673         if(this.modal && this.mask.isVisible()){
30674             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30675             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30676         }
30677         if(this.isVisible()){
30678             this.constrainXY();
30679         }
30680     },
30681
30682     /**
30683      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30684      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30685      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30686      */
30687     destroy : function(removeEl){
30688         if(this.isVisible()){
30689             this.animateTarget = null;
30690             this.hide();
30691         }
30692         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30693         if(this.tabs){
30694             this.tabs.destroy(removeEl);
30695         }
30696         Roo.destroy(
30697              this.shim,
30698              this.proxy,
30699              this.resizer,
30700              this.close,
30701              this.mask
30702         );
30703         if(this.dd){
30704             this.dd.unreg();
30705         }
30706         if(this.buttons){
30707            for(var i = 0, len = this.buttons.length; i < len; i++){
30708                this.buttons[i].destroy();
30709            }
30710         }
30711         this.el.removeAllListeners();
30712         if(removeEl === true){
30713             this.el.update("");
30714             this.el.remove();
30715         }
30716         Roo.DialogManager.unregister(this);
30717     },
30718
30719     // private
30720     startMove : function(){
30721         if(this.proxyDrag){
30722             this.proxy.show();
30723         }
30724         if(this.constraintoviewport !== false){
30725             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30726         }
30727     },
30728
30729     // private
30730     endMove : function(){
30731         if(!this.proxyDrag){
30732             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30733         }else{
30734             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30735             this.proxy.hide();
30736         }
30737         this.refreshSize();
30738         this.adjustAssets();
30739         this.focus();
30740         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30741     },
30742
30743     /**
30744      * Brings this dialog to the front of any other visible dialogs
30745      * @return {Roo.BasicDialog} this
30746      */
30747     toFront : function(){
30748         Roo.DialogManager.bringToFront(this);
30749         return this;
30750     },
30751
30752     /**
30753      * Sends this dialog to the back (under) of any other visible dialogs
30754      * @return {Roo.BasicDialog} this
30755      */
30756     toBack : function(){
30757         Roo.DialogManager.sendToBack(this);
30758         return this;
30759     },
30760
30761     /**
30762      * Centers this dialog in the viewport
30763      * @return {Roo.BasicDialog} this
30764      */
30765     center : function(){
30766         var xy = this.el.getCenterXY(true);
30767         this.moveTo(xy[0], xy[1]);
30768         return this;
30769     },
30770
30771     /**
30772      * Moves the dialog's top-left corner to the specified point
30773      * @param {Number} x
30774      * @param {Number} y
30775      * @return {Roo.BasicDialog} this
30776      */
30777     moveTo : function(x, y){
30778         this.xy = [x,y];
30779         if(this.isVisible()){
30780             this.el.setXY(this.xy);
30781             this.adjustAssets();
30782         }
30783         return this;
30784     },
30785
30786     /**
30787      * Aligns the dialog to the specified element
30788      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30789      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30790      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30791      * @return {Roo.BasicDialog} this
30792      */
30793     alignTo : function(element, position, offsets){
30794         this.xy = this.el.getAlignToXY(element, position, offsets);
30795         if(this.isVisible()){
30796             this.el.setXY(this.xy);
30797             this.adjustAssets();
30798         }
30799         return this;
30800     },
30801
30802     /**
30803      * Anchors an element to another element and realigns it when the window is resized.
30804      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30805      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30806      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30807      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30808      * is a number, it is used as the buffer delay (defaults to 50ms).
30809      * @return {Roo.BasicDialog} this
30810      */
30811     anchorTo : function(el, alignment, offsets, monitorScroll){
30812         var action = function(){
30813             this.alignTo(el, alignment, offsets);
30814         };
30815         Roo.EventManager.onWindowResize(action, this);
30816         var tm = typeof monitorScroll;
30817         if(tm != 'undefined'){
30818             Roo.EventManager.on(window, 'scroll', action, this,
30819                 {buffer: tm == 'number' ? monitorScroll : 50});
30820         }
30821         action.call(this);
30822         return this;
30823     },
30824
30825     /**
30826      * Returns true if the dialog is visible
30827      * @return {Boolean}
30828      */
30829     isVisible : function(){
30830         return this.el.isVisible();
30831     },
30832
30833     // private
30834     animHide : function(callback){
30835         var b = Roo.get(this.animateTarget).getBox();
30836         this.proxy.show();
30837         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30838         this.el.hide();
30839         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30840                     this.hideEl.createDelegate(this, [callback]));
30841     },
30842
30843     /**
30844      * Hides the dialog.
30845      * @param {Function} callback (optional) Function to call when the dialog is hidden
30846      * @return {Roo.BasicDialog} this
30847      */
30848     hide : function(callback){
30849         if (this.fireEvent("beforehide", this) === false){
30850             return;
30851         }
30852         if(this.shadow){
30853             this.shadow.hide();
30854         }
30855         if(this.shim) {
30856           this.shim.hide();
30857         }
30858         // sometimes animateTarget seems to get set.. causing problems...
30859         // this just double checks..
30860         if(this.animateTarget && Roo.get(this.animateTarget)) {
30861            this.animHide(callback);
30862         }else{
30863             this.el.hide();
30864             this.hideEl(callback);
30865         }
30866         return this;
30867     },
30868
30869     // private
30870     hideEl : function(callback){
30871         this.proxy.hide();
30872         if(this.modal){
30873             this.mask.hide();
30874             Roo.get(document.body).removeClass("x-body-masked");
30875         }
30876         this.fireEvent("hide", this);
30877         if(typeof callback == "function"){
30878             callback();
30879         }
30880     },
30881
30882     // private
30883     hideAction : function(){
30884         this.setLeft("-10000px");
30885         this.setTop("-10000px");
30886         this.setStyle("visibility", "hidden");
30887     },
30888
30889     // private
30890     refreshSize : function(){
30891         this.size = this.el.getSize();
30892         this.xy = this.el.getXY();
30893         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30894     },
30895
30896     // private
30897     // z-index is managed by the DialogManager and may be overwritten at any time
30898     setZIndex : function(index){
30899         if(this.modal){
30900             this.mask.setStyle("z-index", index);
30901         }
30902         if(this.shim){
30903             this.shim.setStyle("z-index", ++index);
30904         }
30905         if(this.shadow){
30906             this.shadow.setZIndex(++index);
30907         }
30908         this.el.setStyle("z-index", ++index);
30909         if(this.proxy){
30910             this.proxy.setStyle("z-index", ++index);
30911         }
30912         if(this.resizer){
30913             this.resizer.proxy.setStyle("z-index", ++index);
30914         }
30915
30916         this.lastZIndex = index;
30917     },
30918
30919     /**
30920      * Returns the element for this dialog
30921      * @return {Roo.Element} The underlying dialog Element
30922      */
30923     getEl : function(){
30924         return this.el;
30925     }
30926 });
30927
30928 /**
30929  * @class Roo.DialogManager
30930  * Provides global access to BasicDialogs that have been created and
30931  * support for z-indexing (layering) multiple open dialogs.
30932  */
30933 Roo.DialogManager = function(){
30934     var list = {};
30935     var accessList = [];
30936     var front = null;
30937
30938     // private
30939     var sortDialogs = function(d1, d2){
30940         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30941     };
30942
30943     // private
30944     var orderDialogs = function(){
30945         accessList.sort(sortDialogs);
30946         var seed = Roo.DialogManager.zseed;
30947         for(var i = 0, len = accessList.length; i < len; i++){
30948             var dlg = accessList[i];
30949             if(dlg){
30950                 dlg.setZIndex(seed + (i*10));
30951             }
30952         }
30953     };
30954
30955     return {
30956         /**
30957          * The starting z-index for BasicDialogs (defaults to 9000)
30958          * @type Number The z-index value
30959          */
30960         zseed : 9000,
30961
30962         // private
30963         register : function(dlg){
30964             list[dlg.id] = dlg;
30965             accessList.push(dlg);
30966         },
30967
30968         // private
30969         unregister : function(dlg){
30970             delete list[dlg.id];
30971             var i=0;
30972             var len=0;
30973             if(!accessList.indexOf){
30974                 for(  i = 0, len = accessList.length; i < len; i++){
30975                     if(accessList[i] == dlg){
30976                         accessList.splice(i, 1);
30977                         return;
30978                     }
30979                 }
30980             }else{
30981                  i = accessList.indexOf(dlg);
30982                 if(i != -1){
30983                     accessList.splice(i, 1);
30984                 }
30985             }
30986         },
30987
30988         /**
30989          * Gets a registered dialog by id
30990          * @param {String/Object} id The id of the dialog or a dialog
30991          * @return {Roo.BasicDialog} this
30992          */
30993         get : function(id){
30994             return typeof id == "object" ? id : list[id];
30995         },
30996
30997         /**
30998          * Brings the specified dialog to the front
30999          * @param {String/Object} dlg The id of the dialog or a dialog
31000          * @return {Roo.BasicDialog} this
31001          */
31002         bringToFront : function(dlg){
31003             dlg = this.get(dlg);
31004             if(dlg != front){
31005                 front = dlg;
31006                 dlg._lastAccess = new Date().getTime();
31007                 orderDialogs();
31008             }
31009             return dlg;
31010         },
31011
31012         /**
31013          * Sends the specified dialog to the back
31014          * @param {String/Object} dlg The id of the dialog or a dialog
31015          * @return {Roo.BasicDialog} this
31016          */
31017         sendToBack : function(dlg){
31018             dlg = this.get(dlg);
31019             dlg._lastAccess = -(new Date().getTime());
31020             orderDialogs();
31021             return dlg;
31022         },
31023
31024         /**
31025          * Hides all dialogs
31026          */
31027         hideAll : function(){
31028             for(var id in list){
31029                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31030                     list[id].hide();
31031                 }
31032             }
31033         }
31034     };
31035 }();
31036
31037 /**
31038  * @class Roo.LayoutDialog
31039  * @extends Roo.BasicDialog
31040  * Dialog which provides adjustments for working with a layout in a Dialog.
31041  * Add your necessary layout config options to the dialog's config.<br>
31042  * Example usage (including a nested layout):
31043  * <pre><code>
31044 if(!dialog){
31045     dialog = new Roo.LayoutDialog("download-dlg", {
31046         modal: true,
31047         width:600,
31048         height:450,
31049         shadow:true,
31050         minWidth:500,
31051         minHeight:350,
31052         autoTabs:true,
31053         proxyDrag:true,
31054         // layout config merges with the dialog config
31055         center:{
31056             tabPosition: "top",
31057             alwaysShowTabs: true
31058         }
31059     });
31060     dialog.addKeyListener(27, dialog.hide, dialog);
31061     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31062     dialog.addButton("Build It!", this.getDownload, this);
31063
31064     // we can even add nested layouts
31065     var innerLayout = new Roo.BorderLayout("dl-inner", {
31066         east: {
31067             initialSize: 200,
31068             autoScroll:true,
31069             split:true
31070         },
31071         center: {
31072             autoScroll:true
31073         }
31074     });
31075     innerLayout.beginUpdate();
31076     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31077     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31078     innerLayout.endUpdate(true);
31079
31080     var layout = dialog.getLayout();
31081     layout.beginUpdate();
31082     layout.add("center", new Roo.ContentPanel("standard-panel",
31083                         {title: "Download the Source", fitToFrame:true}));
31084     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31085                {title: "Build your own roo.js"}));
31086     layout.getRegion("center").showPanel(sp);
31087     layout.endUpdate();
31088 }
31089 </code></pre>
31090     * @constructor
31091     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31092     * @param {Object} config configuration options
31093   */
31094 Roo.LayoutDialog = function(el, cfg){
31095     
31096     var config=  cfg;
31097     if (typeof(cfg) == 'undefined') {
31098         config = Roo.apply({}, el);
31099         // not sure why we use documentElement here.. - it should always be body.
31100         // IE7 borks horribly if we use documentElement.
31101         // webkit also does not like documentElement - it creates a body element...
31102         el = Roo.get( document.body || document.documentElement ).createChild();
31103         //config.autoCreate = true;
31104     }
31105     
31106     
31107     config.autoTabs = false;
31108     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31109     this.body.setStyle({overflow:"hidden", position:"relative"});
31110     this.layout = new Roo.BorderLayout(this.body.dom, config);
31111     this.layout.monitorWindowResize = false;
31112     this.el.addClass("x-dlg-auto-layout");
31113     // fix case when center region overwrites center function
31114     this.center = Roo.BasicDialog.prototype.center;
31115     this.on("show", this.layout.layout, this.layout, true);
31116     if (config.items) {
31117         var xitems = config.items;
31118         delete config.items;
31119         Roo.each(xitems, this.addxtype, this);
31120     }
31121     
31122     
31123 };
31124 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31125     /**
31126      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31127      * @deprecated
31128      */
31129     endUpdate : function(){
31130         this.layout.endUpdate();
31131     },
31132
31133     /**
31134      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31135      *  @deprecated
31136      */
31137     beginUpdate : function(){
31138         this.layout.beginUpdate();
31139     },
31140
31141     /**
31142      * Get the BorderLayout for this dialog
31143      * @return {Roo.BorderLayout}
31144      */
31145     getLayout : function(){
31146         return this.layout;
31147     },
31148
31149     showEl : function(){
31150         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31151         if(Roo.isIE7){
31152             this.layout.layout();
31153         }
31154     },
31155
31156     // private
31157     // Use the syncHeightBeforeShow config option to control this automatically
31158     syncBodyHeight : function(){
31159         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31160         if(this.layout){this.layout.layout();}
31161     },
31162     
31163       /**
31164      * Add an xtype element (actually adds to the layout.)
31165      * @return {Object} xdata xtype object data.
31166      */
31167     
31168     addxtype : function(c) {
31169         return this.layout.addxtype(c);
31170     }
31171 });/*
31172  * Based on:
31173  * Ext JS Library 1.1.1
31174  * Copyright(c) 2006-2007, Ext JS, LLC.
31175  *
31176  * Originally Released Under LGPL - original licence link has changed is not relivant.
31177  *
31178  * Fork - LGPL
31179  * <script type="text/javascript">
31180  */
31181  
31182 /**
31183  * @class Roo.MessageBox
31184  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31185  * Example usage:
31186  *<pre><code>
31187 // Basic alert:
31188 Roo.Msg.alert('Status', 'Changes saved successfully.');
31189
31190 // Prompt for user data:
31191 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31192     if (btn == 'ok'){
31193         // process text value...
31194     }
31195 });
31196
31197 // Show a dialog using config options:
31198 Roo.Msg.show({
31199    title:'Save Changes?',
31200    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31201    buttons: Roo.Msg.YESNOCANCEL,
31202    fn: processResult,
31203    animEl: 'elId'
31204 });
31205 </code></pre>
31206  * @singleton
31207  */
31208 Roo.MessageBox = function(){
31209     var dlg, opt, mask, waitTimer;
31210     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31211     var buttons, activeTextEl, bwidth;
31212
31213     // private
31214     var handleButton = function(button){
31215         dlg.hide();
31216         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31217     };
31218
31219     // private
31220     var handleHide = function(){
31221         if(opt && opt.cls){
31222             dlg.el.removeClass(opt.cls);
31223         }
31224         if(waitTimer){
31225             Roo.TaskMgr.stop(waitTimer);
31226             waitTimer = null;
31227         }
31228     };
31229
31230     // private
31231     var updateButtons = function(b){
31232         var width = 0;
31233         if(!b){
31234             buttons["ok"].hide();
31235             buttons["cancel"].hide();
31236             buttons["yes"].hide();
31237             buttons["no"].hide();
31238             dlg.footer.dom.style.display = 'none';
31239             return width;
31240         }
31241         dlg.footer.dom.style.display = '';
31242         for(var k in buttons){
31243             if(typeof buttons[k] != "function"){
31244                 if(b[k]){
31245                     buttons[k].show();
31246                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31247                     width += buttons[k].el.getWidth()+15;
31248                 }else{
31249                     buttons[k].hide();
31250                 }
31251             }
31252         }
31253         return width;
31254     };
31255
31256     // private
31257     var handleEsc = function(d, k, e){
31258         if(opt && opt.closable !== false){
31259             dlg.hide();
31260         }
31261         if(e){
31262             e.stopEvent();
31263         }
31264     };
31265
31266     return {
31267         /**
31268          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31269          * @return {Roo.BasicDialog} The BasicDialog element
31270          */
31271         getDialog : function(){
31272            if(!dlg){
31273                 dlg = new Roo.BasicDialog("x-msg-box", {
31274                     autoCreate : true,
31275                     shadow: true,
31276                     draggable: true,
31277                     resizable:false,
31278                     constraintoviewport:false,
31279                     fixedcenter:true,
31280                     collapsible : false,
31281                     shim:true,
31282                     modal: true,
31283                     width:400, height:100,
31284                     buttonAlign:"center",
31285                     closeClick : function(){
31286                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31287                             handleButton("no");
31288                         }else{
31289                             handleButton("cancel");
31290                         }
31291                     }
31292                 });
31293                 dlg.on("hide", handleHide);
31294                 mask = dlg.mask;
31295                 dlg.addKeyListener(27, handleEsc);
31296                 buttons = {};
31297                 var bt = this.buttonText;
31298                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31299                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31300                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31301                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31302                 bodyEl = dlg.body.createChild({
31303
31304                     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>'
31305                 });
31306                 msgEl = bodyEl.dom.firstChild;
31307                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31308                 textboxEl.enableDisplayMode();
31309                 textboxEl.addKeyListener([10,13], function(){
31310                     if(dlg.isVisible() && opt && opt.buttons){
31311                         if(opt.buttons.ok){
31312                             handleButton("ok");
31313                         }else if(opt.buttons.yes){
31314                             handleButton("yes");
31315                         }
31316                     }
31317                 });
31318                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31319                 textareaEl.enableDisplayMode();
31320                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31321                 progressEl.enableDisplayMode();
31322                 var pf = progressEl.dom.firstChild;
31323                 if (pf) {
31324                     pp = Roo.get(pf.firstChild);
31325                     pp.setHeight(pf.offsetHeight);
31326                 }
31327                 
31328             }
31329             return dlg;
31330         },
31331
31332         /**
31333          * Updates the message box body text
31334          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31335          * the XHTML-compliant non-breaking space character '&amp;#160;')
31336          * @return {Roo.MessageBox} This message box
31337          */
31338         updateText : function(text){
31339             if(!dlg.isVisible() && !opt.width){
31340                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31341             }
31342             msgEl.innerHTML = text || '&#160;';
31343       
31344             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31345             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31346             var w = Math.max(
31347                     Math.min(opt.width || cw , this.maxWidth), 
31348                     Math.max(opt.minWidth || this.minWidth, bwidth)
31349             );
31350             if(opt.prompt){
31351                 activeTextEl.setWidth(w);
31352             }
31353             if(dlg.isVisible()){
31354                 dlg.fixedcenter = false;
31355             }
31356             // to big, make it scroll. = But as usual stupid IE does not support
31357             // !important..
31358             
31359             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31360                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31361                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31362             } else {
31363                 bodyEl.dom.style.height = '';
31364                 bodyEl.dom.style.overflowY = '';
31365             }
31366             if (cw > w) {
31367                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31368             } else {
31369                 bodyEl.dom.style.overflowX = '';
31370             }
31371             
31372             dlg.setContentSize(w, bodyEl.getHeight());
31373             if(dlg.isVisible()){
31374                 dlg.fixedcenter = true;
31375             }
31376             return this;
31377         },
31378
31379         /**
31380          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31381          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31382          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31383          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31384          * @return {Roo.MessageBox} This message box
31385          */
31386         updateProgress : function(value, text){
31387             if(text){
31388                 this.updateText(text);
31389             }
31390             if (pp) { // weird bug on my firefox - for some reason this is not defined
31391                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31392             }
31393             return this;
31394         },        
31395
31396         /**
31397          * Returns true if the message box is currently displayed
31398          * @return {Boolean} True if the message box is visible, else false
31399          */
31400         isVisible : function(){
31401             return dlg && dlg.isVisible();  
31402         },
31403
31404         /**
31405          * Hides the message box if it is displayed
31406          */
31407         hide : function(){
31408             if(this.isVisible()){
31409                 dlg.hide();
31410             }  
31411         },
31412
31413         /**
31414          * Displays a new message box, or reinitializes an existing message box, based on the config options
31415          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31416          * The following config object properties are supported:
31417          * <pre>
31418 Property    Type             Description
31419 ----------  ---------------  ------------------------------------------------------------------------------------
31420 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31421                                    closes (defaults to undefined)
31422 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31423                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31424 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31425                                    progress and wait dialogs will ignore this property and always hide the
31426                                    close button as they can only be closed programmatically.
31427 cls               String           A custom CSS class to apply to the message box element
31428 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31429                                    displayed (defaults to 75)
31430 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31431                                    function will be btn (the name of the button that was clicked, if applicable,
31432                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31433                                    Progress and wait dialogs will ignore this option since they do not respond to
31434                                    user actions and can only be closed programmatically, so any required function
31435                                    should be called by the same code after it closes the dialog.
31436 icon              String           A CSS class that provides a background image to be used as an icon for
31437                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31438 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31439 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31440 modal             Boolean          False to allow user interaction with the page while the message box is
31441                                    displayed (defaults to true)
31442 msg               String           A string that will replace the existing message box body text (defaults
31443                                    to the XHTML-compliant non-breaking space character '&#160;')
31444 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31445 progress          Boolean          True to display a progress bar (defaults to false)
31446 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31447 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31448 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31449 title             String           The title text
31450 value             String           The string value to set into the active textbox element if displayed
31451 wait              Boolean          True to display a progress bar (defaults to false)
31452 width             Number           The width of the dialog in pixels
31453 </pre>
31454          *
31455          * Example usage:
31456          * <pre><code>
31457 Roo.Msg.show({
31458    title: 'Address',
31459    msg: 'Please enter your address:',
31460    width: 300,
31461    buttons: Roo.MessageBox.OKCANCEL,
31462    multiline: true,
31463    fn: saveAddress,
31464    animEl: 'addAddressBtn'
31465 });
31466 </code></pre>
31467          * @param {Object} config Configuration options
31468          * @return {Roo.MessageBox} This message box
31469          */
31470         show : function(options)
31471         {
31472             
31473             // this causes nightmares if you show one dialog after another
31474             // especially on callbacks..
31475              
31476             if(this.isVisible()){
31477                 
31478                 this.hide();
31479                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31480                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31481                 Roo.log("New Dialog Message:" +  options.msg )
31482                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31483                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31484                 
31485             }
31486             var d = this.getDialog();
31487             opt = options;
31488             d.setTitle(opt.title || "&#160;");
31489             d.close.setDisplayed(opt.closable !== false);
31490             activeTextEl = textboxEl;
31491             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31492             if(opt.prompt){
31493                 if(opt.multiline){
31494                     textboxEl.hide();
31495                     textareaEl.show();
31496                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31497                         opt.multiline : this.defaultTextHeight);
31498                     activeTextEl = textareaEl;
31499                 }else{
31500                     textboxEl.show();
31501                     textareaEl.hide();
31502                 }
31503             }else{
31504                 textboxEl.hide();
31505                 textareaEl.hide();
31506             }
31507             progressEl.setDisplayed(opt.progress === true);
31508             this.updateProgress(0);
31509             activeTextEl.dom.value = opt.value || "";
31510             if(opt.prompt){
31511                 dlg.setDefaultButton(activeTextEl);
31512             }else{
31513                 var bs = opt.buttons;
31514                 var db = null;
31515                 if(bs && bs.ok){
31516                     db = buttons["ok"];
31517                 }else if(bs && bs.yes){
31518                     db = buttons["yes"];
31519                 }
31520                 dlg.setDefaultButton(db);
31521             }
31522             bwidth = updateButtons(opt.buttons);
31523             this.updateText(opt.msg);
31524             if(opt.cls){
31525                 d.el.addClass(opt.cls);
31526             }
31527             d.proxyDrag = opt.proxyDrag === true;
31528             d.modal = opt.modal !== false;
31529             d.mask = opt.modal !== false ? mask : false;
31530             if(!d.isVisible()){
31531                 // force it to the end of the z-index stack so it gets a cursor in FF
31532                 document.body.appendChild(dlg.el.dom);
31533                 d.animateTarget = null;
31534                 d.show(options.animEl);
31535             }
31536             return this;
31537         },
31538
31539         /**
31540          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31541          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31542          * and closing the message box when the process is complete.
31543          * @param {String} title The title bar text
31544          * @param {String} msg The message box body text
31545          * @return {Roo.MessageBox} This message box
31546          */
31547         progress : function(title, msg){
31548             this.show({
31549                 title : title,
31550                 msg : msg,
31551                 buttons: false,
31552                 progress:true,
31553                 closable:false,
31554                 minWidth: this.minProgressWidth,
31555                 modal : true
31556             });
31557             return this;
31558         },
31559
31560         /**
31561          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31562          * If a callback function is passed it will be called after the user clicks the button, and the
31563          * id of the button that was clicked will be passed as the only parameter to the callback
31564          * (could also be the top-right close button).
31565          * @param {String} title The title bar text
31566          * @param {String} msg The message box body text
31567          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31568          * @param {Object} scope (optional) The scope of the callback function
31569          * @return {Roo.MessageBox} This message box
31570          */
31571         alert : function(title, msg, fn, scope){
31572             this.show({
31573                 title : title,
31574                 msg : msg,
31575                 buttons: this.OK,
31576                 fn: fn,
31577                 scope : scope,
31578                 modal : true
31579             });
31580             return this;
31581         },
31582
31583         /**
31584          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31585          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31586          * You are responsible for closing the message box when the process is complete.
31587          * @param {String} msg The message box body text
31588          * @param {String} title (optional) The title bar text
31589          * @return {Roo.MessageBox} This message box
31590          */
31591         wait : function(msg, title){
31592             this.show({
31593                 title : title,
31594                 msg : msg,
31595                 buttons: false,
31596                 closable:false,
31597                 progress:true,
31598                 modal:true,
31599                 width:300,
31600                 wait:true
31601             });
31602             waitTimer = Roo.TaskMgr.start({
31603                 run: function(i){
31604                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31605                 },
31606                 interval: 1000
31607             });
31608             return this;
31609         },
31610
31611         /**
31612          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31613          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31614          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31615          * @param {String} title The title bar text
31616          * @param {String} msg The message box body text
31617          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31618          * @param {Object} scope (optional) The scope of the callback function
31619          * @return {Roo.MessageBox} This message box
31620          */
31621         confirm : function(title, msg, fn, scope){
31622             this.show({
31623                 title : title,
31624                 msg : msg,
31625                 buttons: this.YESNO,
31626                 fn: fn,
31627                 scope : scope,
31628                 modal : true
31629             });
31630             return this;
31631         },
31632
31633         /**
31634          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31635          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31636          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31637          * (could also be the top-right close button) and the text that was entered will be passed as the two
31638          * parameters to the callback.
31639          * @param {String} title The title bar text
31640          * @param {String} msg The message box body text
31641          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31642          * @param {Object} scope (optional) The scope of the callback function
31643          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31644          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31645          * @return {Roo.MessageBox} This message box
31646          */
31647         prompt : function(title, msg, fn, scope, multiline){
31648             this.show({
31649                 title : title,
31650                 msg : msg,
31651                 buttons: this.OKCANCEL,
31652                 fn: fn,
31653                 minWidth:250,
31654                 scope : scope,
31655                 prompt:true,
31656                 multiline: multiline,
31657                 modal : true
31658             });
31659             return this;
31660         },
31661
31662         /**
31663          * Button config that displays a single OK button
31664          * @type Object
31665          */
31666         OK : {ok:true},
31667         /**
31668          * Button config that displays Yes and No buttons
31669          * @type Object
31670          */
31671         YESNO : {yes:true, no:true},
31672         /**
31673          * Button config that displays OK and Cancel buttons
31674          * @type Object
31675          */
31676         OKCANCEL : {ok:true, cancel:true},
31677         /**
31678          * Button config that displays Yes, No and Cancel buttons
31679          * @type Object
31680          */
31681         YESNOCANCEL : {yes:true, no:true, cancel:true},
31682
31683         /**
31684          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31685          * @type Number
31686          */
31687         defaultTextHeight : 75,
31688         /**
31689          * The maximum width in pixels of the message box (defaults to 600)
31690          * @type Number
31691          */
31692         maxWidth : 600,
31693         /**
31694          * The minimum width in pixels of the message box (defaults to 100)
31695          * @type Number
31696          */
31697         minWidth : 100,
31698         /**
31699          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31700          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31701          * @type Number
31702          */
31703         minProgressWidth : 250,
31704         /**
31705          * An object containing the default button text strings that can be overriden for localized language support.
31706          * Supported properties are: ok, cancel, yes and no.
31707          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31708          * @type Object
31709          */
31710         buttonText : {
31711             ok : "OK",
31712             cancel : "Cancel",
31713             yes : "Yes",
31714             no : "No"
31715         }
31716     };
31717 }();
31718
31719 /**
31720  * Shorthand for {@link Roo.MessageBox}
31721  */
31722 Roo.Msg = Roo.MessageBox;/*
31723  * Based on:
31724  * Ext JS Library 1.1.1
31725  * Copyright(c) 2006-2007, Ext JS, LLC.
31726  *
31727  * Originally Released Under LGPL - original licence link has changed is not relivant.
31728  *
31729  * Fork - LGPL
31730  * <script type="text/javascript">
31731  */
31732 /**
31733  * @class Roo.QuickTips
31734  * Provides attractive and customizable tooltips for any element.
31735  * @singleton
31736  */
31737 Roo.QuickTips = function(){
31738     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31739     var ce, bd, xy, dd;
31740     var visible = false, disabled = true, inited = false;
31741     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31742     
31743     var onOver = function(e){
31744         if(disabled){
31745             return;
31746         }
31747         var t = e.getTarget();
31748         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31749             return;
31750         }
31751         if(ce && t == ce.el){
31752             clearTimeout(hideProc);
31753             return;
31754         }
31755         if(t && tagEls[t.id]){
31756             tagEls[t.id].el = t;
31757             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31758             return;
31759         }
31760         var ttp, et = Roo.fly(t);
31761         var ns = cfg.namespace;
31762         if(tm.interceptTitles && t.title){
31763             ttp = t.title;
31764             t.qtip = ttp;
31765             t.removeAttribute("title");
31766             e.preventDefault();
31767         }else{
31768             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31769         }
31770         if(ttp){
31771             showProc = show.defer(tm.showDelay, tm, [{
31772                 el: t, 
31773                 text: ttp, 
31774                 width: et.getAttributeNS(ns, cfg.width),
31775                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31776                 title: et.getAttributeNS(ns, cfg.title),
31777                     cls: et.getAttributeNS(ns, cfg.cls)
31778             }]);
31779         }
31780     };
31781     
31782     var onOut = function(e){
31783         clearTimeout(showProc);
31784         var t = e.getTarget();
31785         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31786             hideProc = setTimeout(hide, tm.hideDelay);
31787         }
31788     };
31789     
31790     var onMove = function(e){
31791         if(disabled){
31792             return;
31793         }
31794         xy = e.getXY();
31795         xy[1] += 18;
31796         if(tm.trackMouse && ce){
31797             el.setXY(xy);
31798         }
31799     };
31800     
31801     var onDown = function(e){
31802         clearTimeout(showProc);
31803         clearTimeout(hideProc);
31804         if(!e.within(el)){
31805             if(tm.hideOnClick){
31806                 hide();
31807                 tm.disable();
31808                 tm.enable.defer(100, tm);
31809             }
31810         }
31811     };
31812     
31813     var getPad = function(){
31814         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31815     };
31816
31817     var show = function(o){
31818         if(disabled){
31819             return;
31820         }
31821         clearTimeout(dismissProc);
31822         ce = o;
31823         if(removeCls){ // in case manually hidden
31824             el.removeClass(removeCls);
31825             removeCls = null;
31826         }
31827         if(ce.cls){
31828             el.addClass(ce.cls);
31829             removeCls = ce.cls;
31830         }
31831         if(ce.title){
31832             tipTitle.update(ce.title);
31833             tipTitle.show();
31834         }else{
31835             tipTitle.update('');
31836             tipTitle.hide();
31837         }
31838         el.dom.style.width  = tm.maxWidth+'px';
31839         //tipBody.dom.style.width = '';
31840         tipBodyText.update(o.text);
31841         var p = getPad(), w = ce.width;
31842         if(!w){
31843             var td = tipBodyText.dom;
31844             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31845             if(aw > tm.maxWidth){
31846                 w = tm.maxWidth;
31847             }else if(aw < tm.minWidth){
31848                 w = tm.minWidth;
31849             }else{
31850                 w = aw;
31851             }
31852         }
31853         //tipBody.setWidth(w);
31854         el.setWidth(parseInt(w, 10) + p);
31855         if(ce.autoHide === false){
31856             close.setDisplayed(true);
31857             if(dd){
31858                 dd.unlock();
31859             }
31860         }else{
31861             close.setDisplayed(false);
31862             if(dd){
31863                 dd.lock();
31864             }
31865         }
31866         if(xy){
31867             el.avoidY = xy[1]-18;
31868             el.setXY(xy);
31869         }
31870         if(tm.animate){
31871             el.setOpacity(.1);
31872             el.setStyle("visibility", "visible");
31873             el.fadeIn({callback: afterShow});
31874         }else{
31875             afterShow();
31876         }
31877     };
31878     
31879     var afterShow = function(){
31880         if(ce){
31881             el.show();
31882             esc.enable();
31883             if(tm.autoDismiss && ce.autoHide !== false){
31884                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31885             }
31886         }
31887     };
31888     
31889     var hide = function(noanim){
31890         clearTimeout(dismissProc);
31891         clearTimeout(hideProc);
31892         ce = null;
31893         if(el.isVisible()){
31894             esc.disable();
31895             if(noanim !== true && tm.animate){
31896                 el.fadeOut({callback: afterHide});
31897             }else{
31898                 afterHide();
31899             } 
31900         }
31901     };
31902     
31903     var afterHide = function(){
31904         el.hide();
31905         if(removeCls){
31906             el.removeClass(removeCls);
31907             removeCls = null;
31908         }
31909     };
31910     
31911     return {
31912         /**
31913         * @cfg {Number} minWidth
31914         * The minimum width of the quick tip (defaults to 40)
31915         */
31916        minWidth : 40,
31917         /**
31918         * @cfg {Number} maxWidth
31919         * The maximum width of the quick tip (defaults to 300)
31920         */
31921        maxWidth : 300,
31922         /**
31923         * @cfg {Boolean} interceptTitles
31924         * True to automatically use the element's DOM title value if available (defaults to false)
31925         */
31926        interceptTitles : false,
31927         /**
31928         * @cfg {Boolean} trackMouse
31929         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31930         */
31931        trackMouse : false,
31932         /**
31933         * @cfg {Boolean} hideOnClick
31934         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31935         */
31936        hideOnClick : true,
31937         /**
31938         * @cfg {Number} showDelay
31939         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31940         */
31941        showDelay : 500,
31942         /**
31943         * @cfg {Number} hideDelay
31944         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31945         */
31946        hideDelay : 200,
31947         /**
31948         * @cfg {Boolean} autoHide
31949         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31950         * Used in conjunction with hideDelay.
31951         */
31952        autoHide : true,
31953         /**
31954         * @cfg {Boolean}
31955         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31956         * (defaults to true).  Used in conjunction with autoDismissDelay.
31957         */
31958        autoDismiss : true,
31959         /**
31960         * @cfg {Number}
31961         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31962         */
31963        autoDismissDelay : 5000,
31964        /**
31965         * @cfg {Boolean} animate
31966         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31967         */
31968        animate : false,
31969
31970        /**
31971         * @cfg {String} title
31972         * Title text to display (defaults to '').  This can be any valid HTML markup.
31973         */
31974         title: '',
31975        /**
31976         * @cfg {String} text
31977         * Body text to display (defaults to '').  This can be any valid HTML markup.
31978         */
31979         text : '',
31980        /**
31981         * @cfg {String} cls
31982         * A CSS class to apply to the base quick tip element (defaults to '').
31983         */
31984         cls : '',
31985        /**
31986         * @cfg {Number} width
31987         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31988         * minWidth or maxWidth.
31989         */
31990         width : null,
31991
31992     /**
31993      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31994      * or display QuickTips in a page.
31995      */
31996        init : function(){
31997           tm = Roo.QuickTips;
31998           cfg = tm.tagConfig;
31999           if(!inited){
32000               if(!Roo.isReady){ // allow calling of init() before onReady
32001                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32002                   return;
32003               }
32004               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32005               el.fxDefaults = {stopFx: true};
32006               // maximum custom styling
32007               //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>');
32008               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>');              
32009               tipTitle = el.child('h3');
32010               tipTitle.enableDisplayMode("block");
32011               tipBody = el.child('div.x-tip-bd');
32012               tipBodyText = el.child('div.x-tip-bd-inner');
32013               //bdLeft = el.child('div.x-tip-bd-left');
32014               //bdRight = el.child('div.x-tip-bd-right');
32015               close = el.child('div.x-tip-close');
32016               close.enableDisplayMode("block");
32017               close.on("click", hide);
32018               var d = Roo.get(document);
32019               d.on("mousedown", onDown);
32020               d.on("mouseover", onOver);
32021               d.on("mouseout", onOut);
32022               d.on("mousemove", onMove);
32023               esc = d.addKeyListener(27, hide);
32024               esc.disable();
32025               if(Roo.dd.DD){
32026                   dd = el.initDD("default", null, {
32027                       onDrag : function(){
32028                           el.sync();  
32029                       }
32030                   });
32031                   dd.setHandleElId(tipTitle.id);
32032                   dd.lock();
32033               }
32034               inited = true;
32035           }
32036           this.enable(); 
32037        },
32038
32039     /**
32040      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32041      * are supported:
32042      * <pre>
32043 Property    Type                   Description
32044 ----------  ---------------------  ------------------------------------------------------------------------
32045 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32046      * </ul>
32047      * @param {Object} config The config object
32048      */
32049        register : function(config){
32050            var cs = config instanceof Array ? config : arguments;
32051            for(var i = 0, len = cs.length; i < len; i++) {
32052                var c = cs[i];
32053                var target = c.target;
32054                if(target){
32055                    if(target instanceof Array){
32056                        for(var j = 0, jlen = target.length; j < jlen; j++){
32057                            tagEls[target[j]] = c;
32058                        }
32059                    }else{
32060                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32061                    }
32062                }
32063            }
32064        },
32065
32066     /**
32067      * Removes this quick tip from its element and destroys it.
32068      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32069      */
32070        unregister : function(el){
32071            delete tagEls[Roo.id(el)];
32072        },
32073
32074     /**
32075      * Enable this quick tip.
32076      */
32077        enable : function(){
32078            if(inited && disabled){
32079                locks.pop();
32080                if(locks.length < 1){
32081                    disabled = false;
32082                }
32083            }
32084        },
32085
32086     /**
32087      * Disable this quick tip.
32088      */
32089        disable : function(){
32090           disabled = true;
32091           clearTimeout(showProc);
32092           clearTimeout(hideProc);
32093           clearTimeout(dismissProc);
32094           if(ce){
32095               hide(true);
32096           }
32097           locks.push(1);
32098        },
32099
32100     /**
32101      * Returns true if the quick tip is enabled, else false.
32102      */
32103        isEnabled : function(){
32104             return !disabled;
32105        },
32106
32107         // private
32108        tagConfig : {
32109            namespace : "ext",
32110            attribute : "qtip",
32111            width : "width",
32112            target : "target",
32113            title : "qtitle",
32114            hide : "hide",
32115            cls : "qclass"
32116        }
32117    };
32118 }();
32119
32120 // backwards compat
32121 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32122  * Based on:
32123  * Ext JS Library 1.1.1
32124  * Copyright(c) 2006-2007, Ext JS, LLC.
32125  *
32126  * Originally Released Under LGPL - original licence link has changed is not relivant.
32127  *
32128  * Fork - LGPL
32129  * <script type="text/javascript">
32130  */
32131  
32132
32133 /**
32134  * @class Roo.tree.TreePanel
32135  * @extends Roo.data.Tree
32136
32137  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32138  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32139  * @cfg {Boolean} enableDD true to enable drag and drop
32140  * @cfg {Boolean} enableDrag true to enable just drag
32141  * @cfg {Boolean} enableDrop true to enable just drop
32142  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32143  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32144  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32145  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32146  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32147  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32148  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32149  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32150  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32151  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32152  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32153  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32154  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32155  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32156  * @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>
32157  * @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>
32158  * 
32159  * @constructor
32160  * @param {String/HTMLElement/Element} el The container element
32161  * @param {Object} config
32162  */
32163 Roo.tree.TreePanel = function(el, config){
32164     var root = false;
32165     var loader = false;
32166     if (config.root) {
32167         root = config.root;
32168         delete config.root;
32169     }
32170     if (config.loader) {
32171         loader = config.loader;
32172         delete config.loader;
32173     }
32174     
32175     Roo.apply(this, config);
32176     Roo.tree.TreePanel.superclass.constructor.call(this);
32177     this.el = Roo.get(el);
32178     this.el.addClass('x-tree');
32179     //console.log(root);
32180     if (root) {
32181         this.setRootNode( Roo.factory(root, Roo.tree));
32182     }
32183     if (loader) {
32184         this.loader = Roo.factory(loader, Roo.tree);
32185     }
32186    /**
32187     * Read-only. The id of the container element becomes this TreePanel's id.
32188     */
32189     this.id = this.el.id;
32190     this.addEvents({
32191         /**
32192         * @event beforeload
32193         * Fires before a node is loaded, return false to cancel
32194         * @param {Node} node The node being loaded
32195         */
32196         "beforeload" : true,
32197         /**
32198         * @event load
32199         * Fires when a node is loaded
32200         * @param {Node} node The node that was loaded
32201         */
32202         "load" : true,
32203         /**
32204         * @event textchange
32205         * Fires when the text for a node is changed
32206         * @param {Node} node The node
32207         * @param {String} text The new text
32208         * @param {String} oldText The old text
32209         */
32210         "textchange" : true,
32211         /**
32212         * @event beforeexpand
32213         * Fires before a node is expanded, return false to cancel.
32214         * @param {Node} node The node
32215         * @param {Boolean} deep
32216         * @param {Boolean} anim
32217         */
32218         "beforeexpand" : true,
32219         /**
32220         * @event beforecollapse
32221         * Fires before a node is collapsed, return false to cancel.
32222         * @param {Node} node The node
32223         * @param {Boolean} deep
32224         * @param {Boolean} anim
32225         */
32226         "beforecollapse" : true,
32227         /**
32228         * @event expand
32229         * Fires when a node is expanded
32230         * @param {Node} node The node
32231         */
32232         "expand" : true,
32233         /**
32234         * @event disabledchange
32235         * Fires when the disabled status of a node changes
32236         * @param {Node} node The node
32237         * @param {Boolean} disabled
32238         */
32239         "disabledchange" : true,
32240         /**
32241         * @event collapse
32242         * Fires when a node is collapsed
32243         * @param {Node} node The node
32244         */
32245         "collapse" : true,
32246         /**
32247         * @event beforeclick
32248         * Fires before click processing on a node. Return false to cancel the default action.
32249         * @param {Node} node The node
32250         * @param {Roo.EventObject} e The event object
32251         */
32252         "beforeclick":true,
32253         /**
32254         * @event checkchange
32255         * Fires when a node with a checkbox's checked property changes
32256         * @param {Node} this This node
32257         * @param {Boolean} checked
32258         */
32259         "checkchange":true,
32260         /**
32261         * @event click
32262         * Fires when a node is clicked
32263         * @param {Node} node The node
32264         * @param {Roo.EventObject} e The event object
32265         */
32266         "click":true,
32267         /**
32268         * @event dblclick
32269         * Fires when a node is double clicked
32270         * @param {Node} node The node
32271         * @param {Roo.EventObject} e The event object
32272         */
32273         "dblclick":true,
32274         /**
32275         * @event contextmenu
32276         * Fires when a node is right clicked
32277         * @param {Node} node The node
32278         * @param {Roo.EventObject} e The event object
32279         */
32280         "contextmenu":true,
32281         /**
32282         * @event beforechildrenrendered
32283         * Fires right before the child nodes for a node are rendered
32284         * @param {Node} node The node
32285         */
32286         "beforechildrenrendered":true,
32287         /**
32288         * @event startdrag
32289         * Fires when a node starts being dragged
32290         * @param {Roo.tree.TreePanel} this
32291         * @param {Roo.tree.TreeNode} node
32292         * @param {event} e The raw browser event
32293         */ 
32294        "startdrag" : true,
32295        /**
32296         * @event enddrag
32297         * Fires when a drag operation is complete
32298         * @param {Roo.tree.TreePanel} this
32299         * @param {Roo.tree.TreeNode} node
32300         * @param {event} e The raw browser event
32301         */
32302        "enddrag" : true,
32303        /**
32304         * @event dragdrop
32305         * Fires when a dragged node is dropped on a valid DD target
32306         * @param {Roo.tree.TreePanel} this
32307         * @param {Roo.tree.TreeNode} node
32308         * @param {DD} dd The dd it was dropped on
32309         * @param {event} e The raw browser event
32310         */
32311        "dragdrop" : true,
32312        /**
32313         * @event beforenodedrop
32314         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32315         * passed to handlers has the following properties:<br />
32316         * <ul style="padding:5px;padding-left:16px;">
32317         * <li>tree - The TreePanel</li>
32318         * <li>target - The node being targeted for the drop</li>
32319         * <li>data - The drag data from the drag source</li>
32320         * <li>point - The point of the drop - append, above or below</li>
32321         * <li>source - The drag source</li>
32322         * <li>rawEvent - Raw mouse event</li>
32323         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32324         * to be inserted by setting them on this object.</li>
32325         * <li>cancel - Set this to true to cancel the drop.</li>
32326         * </ul>
32327         * @param {Object} dropEvent
32328         */
32329        "beforenodedrop" : true,
32330        /**
32331         * @event nodedrop
32332         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32333         * passed to handlers has the following properties:<br />
32334         * <ul style="padding:5px;padding-left:16px;">
32335         * <li>tree - The TreePanel</li>
32336         * <li>target - The node being targeted for the drop</li>
32337         * <li>data - The drag data from the drag source</li>
32338         * <li>point - The point of the drop - append, above or below</li>
32339         * <li>source - The drag source</li>
32340         * <li>rawEvent - Raw mouse event</li>
32341         * <li>dropNode - Dropped node(s).</li>
32342         * </ul>
32343         * @param {Object} dropEvent
32344         */
32345        "nodedrop" : true,
32346         /**
32347         * @event nodedragover
32348         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32349         * passed to handlers has the following properties:<br />
32350         * <ul style="padding:5px;padding-left:16px;">
32351         * <li>tree - The TreePanel</li>
32352         * <li>target - The node being targeted for the drop</li>
32353         * <li>data - The drag data from the drag source</li>
32354         * <li>point - The point of the drop - append, above or below</li>
32355         * <li>source - The drag source</li>
32356         * <li>rawEvent - Raw mouse event</li>
32357         * <li>dropNode - Drop node(s) provided by the source.</li>
32358         * <li>cancel - Set this to true to signal drop not allowed.</li>
32359         * </ul>
32360         * @param {Object} dragOverEvent
32361         */
32362        "nodedragover" : true
32363         
32364     });
32365     if(this.singleExpand){
32366        this.on("beforeexpand", this.restrictExpand, this);
32367     }
32368     if (this.editor) {
32369         this.editor.tree = this;
32370         this.editor = Roo.factory(this.editor, Roo.tree);
32371     }
32372     
32373     if (this.selModel) {
32374         this.selModel = Roo.factory(this.selModel, Roo.tree);
32375     }
32376    
32377 };
32378 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32379     rootVisible : true,
32380     animate: Roo.enableFx,
32381     lines : true,
32382     enableDD : false,
32383     hlDrop : Roo.enableFx,
32384   
32385     renderer: false,
32386     
32387     rendererTip: false,
32388     // private
32389     restrictExpand : function(node){
32390         var p = node.parentNode;
32391         if(p){
32392             if(p.expandedChild && p.expandedChild.parentNode == p){
32393                 p.expandedChild.collapse();
32394             }
32395             p.expandedChild = node;
32396         }
32397     },
32398
32399     // private override
32400     setRootNode : function(node){
32401         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32402         if(!this.rootVisible){
32403             node.ui = new Roo.tree.RootTreeNodeUI(node);
32404         }
32405         return node;
32406     },
32407
32408     /**
32409      * Returns the container element for this TreePanel
32410      */
32411     getEl : function(){
32412         return this.el;
32413     },
32414
32415     /**
32416      * Returns the default TreeLoader for this TreePanel
32417      */
32418     getLoader : function(){
32419         return this.loader;
32420     },
32421
32422     /**
32423      * Expand all nodes
32424      */
32425     expandAll : function(){
32426         this.root.expand(true);
32427     },
32428
32429     /**
32430      * Collapse all nodes
32431      */
32432     collapseAll : function(){
32433         this.root.collapse(true);
32434     },
32435
32436     /**
32437      * Returns the selection model used by this TreePanel
32438      */
32439     getSelectionModel : function(){
32440         if(!this.selModel){
32441             this.selModel = new Roo.tree.DefaultSelectionModel();
32442         }
32443         return this.selModel;
32444     },
32445
32446     /**
32447      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32448      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32449      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32450      * @return {Array}
32451      */
32452     getChecked : function(a, startNode){
32453         startNode = startNode || this.root;
32454         var r = [];
32455         var f = function(){
32456             if(this.attributes.checked){
32457                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32458             }
32459         }
32460         startNode.cascade(f);
32461         return r;
32462     },
32463
32464     /**
32465      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32466      * @param {String} path
32467      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32468      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32469      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32470      */
32471     expandPath : function(path, attr, callback){
32472         attr = attr || "id";
32473         var keys = path.split(this.pathSeparator);
32474         var curNode = this.root;
32475         if(curNode.attributes[attr] != keys[1]){ // invalid root
32476             if(callback){
32477                 callback(false, null);
32478             }
32479             return;
32480         }
32481         var index = 1;
32482         var f = function(){
32483             if(++index == keys.length){
32484                 if(callback){
32485                     callback(true, curNode);
32486                 }
32487                 return;
32488             }
32489             var c = curNode.findChild(attr, keys[index]);
32490             if(!c){
32491                 if(callback){
32492                     callback(false, curNode);
32493                 }
32494                 return;
32495             }
32496             curNode = c;
32497             c.expand(false, false, f);
32498         };
32499         curNode.expand(false, false, f);
32500     },
32501
32502     /**
32503      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32504      * @param {String} path
32505      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32506      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32507      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32508      */
32509     selectPath : function(path, attr, callback){
32510         attr = attr || "id";
32511         var keys = path.split(this.pathSeparator);
32512         var v = keys.pop();
32513         if(keys.length > 0){
32514             var f = function(success, node){
32515                 if(success && node){
32516                     var n = node.findChild(attr, v);
32517                     if(n){
32518                         n.select();
32519                         if(callback){
32520                             callback(true, n);
32521                         }
32522                     }else if(callback){
32523                         callback(false, n);
32524                     }
32525                 }else{
32526                     if(callback){
32527                         callback(false, n);
32528                     }
32529                 }
32530             };
32531             this.expandPath(keys.join(this.pathSeparator), attr, f);
32532         }else{
32533             this.root.select();
32534             if(callback){
32535                 callback(true, this.root);
32536             }
32537         }
32538     },
32539
32540     getTreeEl : function(){
32541         return this.el;
32542     },
32543
32544     /**
32545      * Trigger rendering of this TreePanel
32546      */
32547     render : function(){
32548         if (this.innerCt) {
32549             return this; // stop it rendering more than once!!
32550         }
32551         
32552         this.innerCt = this.el.createChild({tag:"ul",
32553                cls:"x-tree-root-ct " +
32554                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32555
32556         if(this.containerScroll){
32557             Roo.dd.ScrollManager.register(this.el);
32558         }
32559         if((this.enableDD || this.enableDrop) && !this.dropZone){
32560            /**
32561             * The dropZone used by this tree if drop is enabled
32562             * @type Roo.tree.TreeDropZone
32563             */
32564              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32565                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32566            });
32567         }
32568         if((this.enableDD || this.enableDrag) && !this.dragZone){
32569            /**
32570             * The dragZone used by this tree if drag is enabled
32571             * @type Roo.tree.TreeDragZone
32572             */
32573             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32574                ddGroup: this.ddGroup || "TreeDD",
32575                scroll: this.ddScroll
32576            });
32577         }
32578         this.getSelectionModel().init(this);
32579         if (!this.root) {
32580             Roo.log("ROOT not set in tree");
32581             return this;
32582         }
32583         this.root.render();
32584         if(!this.rootVisible){
32585             this.root.renderChildren();
32586         }
32587         return this;
32588     }
32589 });/*
32590  * Based on:
32591  * Ext JS Library 1.1.1
32592  * Copyright(c) 2006-2007, Ext JS, LLC.
32593  *
32594  * Originally Released Under LGPL - original licence link has changed is not relivant.
32595  *
32596  * Fork - LGPL
32597  * <script type="text/javascript">
32598  */
32599  
32600
32601 /**
32602  * @class Roo.tree.DefaultSelectionModel
32603  * @extends Roo.util.Observable
32604  * The default single selection for a TreePanel.
32605  * @param {Object} cfg Configuration
32606  */
32607 Roo.tree.DefaultSelectionModel = function(cfg){
32608    this.selNode = null;
32609    
32610    
32611    
32612    this.addEvents({
32613        /**
32614         * @event selectionchange
32615         * Fires when the selected node changes
32616         * @param {DefaultSelectionModel} this
32617         * @param {TreeNode} node the new selection
32618         */
32619        "selectionchange" : true,
32620
32621        /**
32622         * @event beforeselect
32623         * Fires before the selected node changes, return false to cancel the change
32624         * @param {DefaultSelectionModel} this
32625         * @param {TreeNode} node the new selection
32626         * @param {TreeNode} node the old selection
32627         */
32628        "beforeselect" : true
32629    });
32630    
32631     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32632 };
32633
32634 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32635     init : function(tree){
32636         this.tree = tree;
32637         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32638         tree.on("click", this.onNodeClick, this);
32639     },
32640     
32641     onNodeClick : function(node, e){
32642         if (e.ctrlKey && this.selNode == node)  {
32643             this.unselect(node);
32644             return;
32645         }
32646         this.select(node);
32647     },
32648     
32649     /**
32650      * Select a node.
32651      * @param {TreeNode} node The node to select
32652      * @return {TreeNode} The selected node
32653      */
32654     select : function(node){
32655         var last = this.selNode;
32656         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32657             if(last){
32658                 last.ui.onSelectedChange(false);
32659             }
32660             this.selNode = node;
32661             node.ui.onSelectedChange(true);
32662             this.fireEvent("selectionchange", this, node, last);
32663         }
32664         return node;
32665     },
32666     
32667     /**
32668      * Deselect a node.
32669      * @param {TreeNode} node The node to unselect
32670      */
32671     unselect : function(node){
32672         if(this.selNode == node){
32673             this.clearSelections();
32674         }    
32675     },
32676     
32677     /**
32678      * Clear all selections
32679      */
32680     clearSelections : function(){
32681         var n = this.selNode;
32682         if(n){
32683             n.ui.onSelectedChange(false);
32684             this.selNode = null;
32685             this.fireEvent("selectionchange", this, null);
32686         }
32687         return n;
32688     },
32689     
32690     /**
32691      * Get the selected node
32692      * @return {TreeNode} The selected node
32693      */
32694     getSelectedNode : function(){
32695         return this.selNode;    
32696     },
32697     
32698     /**
32699      * Returns true if the node is selected
32700      * @param {TreeNode} node The node to check
32701      * @return {Boolean}
32702      */
32703     isSelected : function(node){
32704         return this.selNode == node;  
32705     },
32706
32707     /**
32708      * Selects the node above the selected node in the tree, intelligently walking the nodes
32709      * @return TreeNode The new selection
32710      */
32711     selectPrevious : function(){
32712         var s = this.selNode || this.lastSelNode;
32713         if(!s){
32714             return null;
32715         }
32716         var ps = s.previousSibling;
32717         if(ps){
32718             if(!ps.isExpanded() || ps.childNodes.length < 1){
32719                 return this.select(ps);
32720             } else{
32721                 var lc = ps.lastChild;
32722                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32723                     lc = lc.lastChild;
32724                 }
32725                 return this.select(lc);
32726             }
32727         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32728             return this.select(s.parentNode);
32729         }
32730         return null;
32731     },
32732
32733     /**
32734      * Selects the node above the selected node in the tree, intelligently walking the nodes
32735      * @return TreeNode The new selection
32736      */
32737     selectNext : function(){
32738         var s = this.selNode || this.lastSelNode;
32739         if(!s){
32740             return null;
32741         }
32742         if(s.firstChild && s.isExpanded()){
32743              return this.select(s.firstChild);
32744          }else if(s.nextSibling){
32745              return this.select(s.nextSibling);
32746          }else if(s.parentNode){
32747             var newS = null;
32748             s.parentNode.bubble(function(){
32749                 if(this.nextSibling){
32750                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32751                     return false;
32752                 }
32753             });
32754             return newS;
32755          }
32756         return null;
32757     },
32758
32759     onKeyDown : function(e){
32760         var s = this.selNode || this.lastSelNode;
32761         // undesirable, but required
32762         var sm = this;
32763         if(!s){
32764             return;
32765         }
32766         var k = e.getKey();
32767         switch(k){
32768              case e.DOWN:
32769                  e.stopEvent();
32770                  this.selectNext();
32771              break;
32772              case e.UP:
32773                  e.stopEvent();
32774                  this.selectPrevious();
32775              break;
32776              case e.RIGHT:
32777                  e.preventDefault();
32778                  if(s.hasChildNodes()){
32779                      if(!s.isExpanded()){
32780                          s.expand();
32781                      }else if(s.firstChild){
32782                          this.select(s.firstChild, e);
32783                      }
32784                  }
32785              break;
32786              case e.LEFT:
32787                  e.preventDefault();
32788                  if(s.hasChildNodes() && s.isExpanded()){
32789                      s.collapse();
32790                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32791                      this.select(s.parentNode, e);
32792                  }
32793              break;
32794         };
32795     }
32796 });
32797
32798 /**
32799  * @class Roo.tree.MultiSelectionModel
32800  * @extends Roo.util.Observable
32801  * Multi selection for a TreePanel.
32802  * @param {Object} cfg Configuration
32803  */
32804 Roo.tree.MultiSelectionModel = function(){
32805    this.selNodes = [];
32806    this.selMap = {};
32807    this.addEvents({
32808        /**
32809         * @event selectionchange
32810         * Fires when the selected nodes change
32811         * @param {MultiSelectionModel} this
32812         * @param {Array} nodes Array of the selected nodes
32813         */
32814        "selectionchange" : true
32815    });
32816    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32817    
32818 };
32819
32820 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32821     init : function(tree){
32822         this.tree = tree;
32823         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32824         tree.on("click", this.onNodeClick, this);
32825     },
32826     
32827     onNodeClick : function(node, e){
32828         this.select(node, e, e.ctrlKey);
32829     },
32830     
32831     /**
32832      * Select a node.
32833      * @param {TreeNode} node The node to select
32834      * @param {EventObject} e (optional) An event associated with the selection
32835      * @param {Boolean} keepExisting True to retain existing selections
32836      * @return {TreeNode} The selected node
32837      */
32838     select : function(node, e, keepExisting){
32839         if(keepExisting !== true){
32840             this.clearSelections(true);
32841         }
32842         if(this.isSelected(node)){
32843             this.lastSelNode = node;
32844             return node;
32845         }
32846         this.selNodes.push(node);
32847         this.selMap[node.id] = node;
32848         this.lastSelNode = node;
32849         node.ui.onSelectedChange(true);
32850         this.fireEvent("selectionchange", this, this.selNodes);
32851         return node;
32852     },
32853     
32854     /**
32855      * Deselect a node.
32856      * @param {TreeNode} node The node to unselect
32857      */
32858     unselect : function(node){
32859         if(this.selMap[node.id]){
32860             node.ui.onSelectedChange(false);
32861             var sn = this.selNodes;
32862             var index = -1;
32863             if(sn.indexOf){
32864                 index = sn.indexOf(node);
32865             }else{
32866                 for(var i = 0, len = sn.length; i < len; i++){
32867                     if(sn[i] == node){
32868                         index = i;
32869                         break;
32870                     }
32871                 }
32872             }
32873             if(index != -1){
32874                 this.selNodes.splice(index, 1);
32875             }
32876             delete this.selMap[node.id];
32877             this.fireEvent("selectionchange", this, this.selNodes);
32878         }
32879     },
32880     
32881     /**
32882      * Clear all selections
32883      */
32884     clearSelections : function(suppressEvent){
32885         var sn = this.selNodes;
32886         if(sn.length > 0){
32887             for(var i = 0, len = sn.length; i < len; i++){
32888                 sn[i].ui.onSelectedChange(false);
32889             }
32890             this.selNodes = [];
32891             this.selMap = {};
32892             if(suppressEvent !== true){
32893                 this.fireEvent("selectionchange", this, this.selNodes);
32894             }
32895         }
32896     },
32897     
32898     /**
32899      * Returns true if the node is selected
32900      * @param {TreeNode} node The node to check
32901      * @return {Boolean}
32902      */
32903     isSelected : function(node){
32904         return this.selMap[node.id] ? true : false;  
32905     },
32906     
32907     /**
32908      * Returns an array of the selected nodes
32909      * @return {Array}
32910      */
32911     getSelectedNodes : function(){
32912         return this.selNodes;    
32913     },
32914
32915     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32916
32917     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32918
32919     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32920 });/*
32921  * Based on:
32922  * Ext JS Library 1.1.1
32923  * Copyright(c) 2006-2007, Ext JS, LLC.
32924  *
32925  * Originally Released Under LGPL - original licence link has changed is not relivant.
32926  *
32927  * Fork - LGPL
32928  * <script type="text/javascript">
32929  */
32930  
32931 /**
32932  * @class Roo.tree.TreeNode
32933  * @extends Roo.data.Node
32934  * @cfg {String} text The text for this node
32935  * @cfg {Boolean} expanded true to start the node expanded
32936  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32937  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32938  * @cfg {Boolean} disabled true to start the node disabled
32939  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32940  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32941  * @cfg {String} cls A css class to be added to the node
32942  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32943  * @cfg {String} href URL of the link used for the node (defaults to #)
32944  * @cfg {String} hrefTarget target frame for the link
32945  * @cfg {String} qtip An Ext QuickTip for the node
32946  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32947  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32948  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32949  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32950  * (defaults to undefined with no checkbox rendered)
32951  * @constructor
32952  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32953  */
32954 Roo.tree.TreeNode = function(attributes){
32955     attributes = attributes || {};
32956     if(typeof attributes == "string"){
32957         attributes = {text: attributes};
32958     }
32959     this.childrenRendered = false;
32960     this.rendered = false;
32961     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32962     this.expanded = attributes.expanded === true;
32963     this.isTarget = attributes.isTarget !== false;
32964     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32965     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32966
32967     /**
32968      * Read-only. The text for this node. To change it use setText().
32969      * @type String
32970      */
32971     this.text = attributes.text;
32972     /**
32973      * True if this node is disabled.
32974      * @type Boolean
32975      */
32976     this.disabled = attributes.disabled === true;
32977
32978     this.addEvents({
32979         /**
32980         * @event textchange
32981         * Fires when the text for this node is changed
32982         * @param {Node} this This node
32983         * @param {String} text The new text
32984         * @param {String} oldText The old text
32985         */
32986         "textchange" : true,
32987         /**
32988         * @event beforeexpand
32989         * Fires before this node is expanded, return false to cancel.
32990         * @param {Node} this This node
32991         * @param {Boolean} deep
32992         * @param {Boolean} anim
32993         */
32994         "beforeexpand" : true,
32995         /**
32996         * @event beforecollapse
32997         * Fires before this node is collapsed, return false to cancel.
32998         * @param {Node} this This node
32999         * @param {Boolean} deep
33000         * @param {Boolean} anim
33001         */
33002         "beforecollapse" : true,
33003         /**
33004         * @event expand
33005         * Fires when this node is expanded
33006         * @param {Node} this This node
33007         */
33008         "expand" : true,
33009         /**
33010         * @event disabledchange
33011         * Fires when the disabled status of this node changes
33012         * @param {Node} this This node
33013         * @param {Boolean} disabled
33014         */
33015         "disabledchange" : true,
33016         /**
33017         * @event collapse
33018         * Fires when this node is collapsed
33019         * @param {Node} this This node
33020         */
33021         "collapse" : true,
33022         /**
33023         * @event beforeclick
33024         * Fires before click processing. Return false to cancel the default action.
33025         * @param {Node} this This node
33026         * @param {Roo.EventObject} e The event object
33027         */
33028         "beforeclick":true,
33029         /**
33030         * @event checkchange
33031         * Fires when a node with a checkbox's checked property changes
33032         * @param {Node} this This node
33033         * @param {Boolean} checked
33034         */
33035         "checkchange":true,
33036         /**
33037         * @event click
33038         * Fires when this node is clicked
33039         * @param {Node} this This node
33040         * @param {Roo.EventObject} e The event object
33041         */
33042         "click":true,
33043         /**
33044         * @event dblclick
33045         * Fires when this node is double clicked
33046         * @param {Node} this This node
33047         * @param {Roo.EventObject} e The event object
33048         */
33049         "dblclick":true,
33050         /**
33051         * @event contextmenu
33052         * Fires when this node is right clicked
33053         * @param {Node} this This node
33054         * @param {Roo.EventObject} e The event object
33055         */
33056         "contextmenu":true,
33057         /**
33058         * @event beforechildrenrendered
33059         * Fires right before the child nodes for this node are rendered
33060         * @param {Node} this This node
33061         */
33062         "beforechildrenrendered":true
33063     });
33064
33065     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33066
33067     /**
33068      * Read-only. The UI for this node
33069      * @type TreeNodeUI
33070      */
33071     this.ui = new uiClass(this);
33072     
33073     // finally support items[]
33074     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33075         return;
33076     }
33077     
33078     
33079     Roo.each(this.attributes.items, function(c) {
33080         this.appendChild(Roo.factory(c,Roo.Tree));
33081     }, this);
33082     delete this.attributes.items;
33083     
33084     
33085     
33086 };
33087 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33088     preventHScroll: true,
33089     /**
33090      * Returns true if this node is expanded
33091      * @return {Boolean}
33092      */
33093     isExpanded : function(){
33094         return this.expanded;
33095     },
33096
33097     /**
33098      * Returns the UI object for this node
33099      * @return {TreeNodeUI}
33100      */
33101     getUI : function(){
33102         return this.ui;
33103     },
33104
33105     // private override
33106     setFirstChild : function(node){
33107         var of = this.firstChild;
33108         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33109         if(this.childrenRendered && of && node != of){
33110             of.renderIndent(true, true);
33111         }
33112         if(this.rendered){
33113             this.renderIndent(true, true);
33114         }
33115     },
33116
33117     // private override
33118     setLastChild : function(node){
33119         var ol = this.lastChild;
33120         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33121         if(this.childrenRendered && ol && node != ol){
33122             ol.renderIndent(true, true);
33123         }
33124         if(this.rendered){
33125             this.renderIndent(true, true);
33126         }
33127     },
33128
33129     // these methods are overridden to provide lazy rendering support
33130     // private override
33131     appendChild : function()
33132     {
33133         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33134         if(node && this.childrenRendered){
33135             node.render();
33136         }
33137         this.ui.updateExpandIcon();
33138         return node;
33139     },
33140
33141     // private override
33142     removeChild : function(node){
33143         this.ownerTree.getSelectionModel().unselect(node);
33144         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33145         // if it's been rendered remove dom node
33146         if(this.childrenRendered){
33147             node.ui.remove();
33148         }
33149         if(this.childNodes.length < 1){
33150             this.collapse(false, false);
33151         }else{
33152             this.ui.updateExpandIcon();
33153         }
33154         if(!this.firstChild) {
33155             this.childrenRendered = false;
33156         }
33157         return node;
33158     },
33159
33160     // private override
33161     insertBefore : function(node, refNode){
33162         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33163         if(newNode && refNode && this.childrenRendered){
33164             node.render();
33165         }
33166         this.ui.updateExpandIcon();
33167         return newNode;
33168     },
33169
33170     /**
33171      * Sets the text for this node
33172      * @param {String} text
33173      */
33174     setText : function(text){
33175         var oldText = this.text;
33176         this.text = text;
33177         this.attributes.text = text;
33178         if(this.rendered){ // event without subscribing
33179             this.ui.onTextChange(this, text, oldText);
33180         }
33181         this.fireEvent("textchange", this, text, oldText);
33182     },
33183
33184     /**
33185      * Triggers selection of this node
33186      */
33187     select : function(){
33188         this.getOwnerTree().getSelectionModel().select(this);
33189     },
33190
33191     /**
33192      * Triggers deselection of this node
33193      */
33194     unselect : function(){
33195         this.getOwnerTree().getSelectionModel().unselect(this);
33196     },
33197
33198     /**
33199      * Returns true if this node is selected
33200      * @return {Boolean}
33201      */
33202     isSelected : function(){
33203         return this.getOwnerTree().getSelectionModel().isSelected(this);
33204     },
33205
33206     /**
33207      * Expand this node.
33208      * @param {Boolean} deep (optional) True to expand all children as well
33209      * @param {Boolean} anim (optional) false to cancel the default animation
33210      * @param {Function} callback (optional) A callback to be called when
33211      * expanding this node completes (does not wait for deep expand to complete).
33212      * Called with 1 parameter, this node.
33213      */
33214     expand : function(deep, anim, callback){
33215         if(!this.expanded){
33216             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33217                 return;
33218             }
33219             if(!this.childrenRendered){
33220                 this.renderChildren();
33221             }
33222             this.expanded = true;
33223             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33224                 this.ui.animExpand(function(){
33225                     this.fireEvent("expand", this);
33226                     if(typeof callback == "function"){
33227                         callback(this);
33228                     }
33229                     if(deep === true){
33230                         this.expandChildNodes(true);
33231                     }
33232                 }.createDelegate(this));
33233                 return;
33234             }else{
33235                 this.ui.expand();
33236                 this.fireEvent("expand", this);
33237                 if(typeof callback == "function"){
33238                     callback(this);
33239                 }
33240             }
33241         }else{
33242            if(typeof callback == "function"){
33243                callback(this);
33244            }
33245         }
33246         if(deep === true){
33247             this.expandChildNodes(true);
33248         }
33249     },
33250
33251     isHiddenRoot : function(){
33252         return this.isRoot && !this.getOwnerTree().rootVisible;
33253     },
33254
33255     /**
33256      * Collapse this node.
33257      * @param {Boolean} deep (optional) True to collapse all children as well
33258      * @param {Boolean} anim (optional) false to cancel the default animation
33259      */
33260     collapse : function(deep, anim){
33261         if(this.expanded && !this.isHiddenRoot()){
33262             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33263                 return;
33264             }
33265             this.expanded = false;
33266             if((this.getOwnerTree().animate && anim !== false) || anim){
33267                 this.ui.animCollapse(function(){
33268                     this.fireEvent("collapse", this);
33269                     if(deep === true){
33270                         this.collapseChildNodes(true);
33271                     }
33272                 }.createDelegate(this));
33273                 return;
33274             }else{
33275                 this.ui.collapse();
33276                 this.fireEvent("collapse", this);
33277             }
33278         }
33279         if(deep === true){
33280             var cs = this.childNodes;
33281             for(var i = 0, len = cs.length; i < len; i++) {
33282                 cs[i].collapse(true, false);
33283             }
33284         }
33285     },
33286
33287     // private
33288     delayedExpand : function(delay){
33289         if(!this.expandProcId){
33290             this.expandProcId = this.expand.defer(delay, this);
33291         }
33292     },
33293
33294     // private
33295     cancelExpand : function(){
33296         if(this.expandProcId){
33297             clearTimeout(this.expandProcId);
33298         }
33299         this.expandProcId = false;
33300     },
33301
33302     /**
33303      * Toggles expanded/collapsed state of the node
33304      */
33305     toggle : function(){
33306         if(this.expanded){
33307             this.collapse();
33308         }else{
33309             this.expand();
33310         }
33311     },
33312
33313     /**
33314      * Ensures all parent nodes are expanded
33315      */
33316     ensureVisible : function(callback){
33317         var tree = this.getOwnerTree();
33318         tree.expandPath(this.parentNode.getPath(), false, function(){
33319             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33320             Roo.callback(callback);
33321         }.createDelegate(this));
33322     },
33323
33324     /**
33325      * Expand all child nodes
33326      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33327      */
33328     expandChildNodes : function(deep){
33329         var cs = this.childNodes;
33330         for(var i = 0, len = cs.length; i < len; i++) {
33331                 cs[i].expand(deep);
33332         }
33333     },
33334
33335     /**
33336      * Collapse all child nodes
33337      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33338      */
33339     collapseChildNodes : function(deep){
33340         var cs = this.childNodes;
33341         for(var i = 0, len = cs.length; i < len; i++) {
33342                 cs[i].collapse(deep);
33343         }
33344     },
33345
33346     /**
33347      * Disables this node
33348      */
33349     disable : function(){
33350         this.disabled = true;
33351         this.unselect();
33352         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33353             this.ui.onDisableChange(this, true);
33354         }
33355         this.fireEvent("disabledchange", this, true);
33356     },
33357
33358     /**
33359      * Enables this node
33360      */
33361     enable : function(){
33362         this.disabled = false;
33363         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33364             this.ui.onDisableChange(this, false);
33365         }
33366         this.fireEvent("disabledchange", this, false);
33367     },
33368
33369     // private
33370     renderChildren : function(suppressEvent){
33371         if(suppressEvent !== false){
33372             this.fireEvent("beforechildrenrendered", this);
33373         }
33374         var cs = this.childNodes;
33375         for(var i = 0, len = cs.length; i < len; i++){
33376             cs[i].render(true);
33377         }
33378         this.childrenRendered = true;
33379     },
33380
33381     // private
33382     sort : function(fn, scope){
33383         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33384         if(this.childrenRendered){
33385             var cs = this.childNodes;
33386             for(var i = 0, len = cs.length; i < len; i++){
33387                 cs[i].render(true);
33388             }
33389         }
33390     },
33391
33392     // private
33393     render : function(bulkRender){
33394         this.ui.render(bulkRender);
33395         if(!this.rendered){
33396             this.rendered = true;
33397             if(this.expanded){
33398                 this.expanded = false;
33399                 this.expand(false, false);
33400             }
33401         }
33402     },
33403
33404     // private
33405     renderIndent : function(deep, refresh){
33406         if(refresh){
33407             this.ui.childIndent = null;
33408         }
33409         this.ui.renderIndent();
33410         if(deep === true && this.childrenRendered){
33411             var cs = this.childNodes;
33412             for(var i = 0, len = cs.length; i < len; i++){
33413                 cs[i].renderIndent(true, refresh);
33414             }
33415         }
33416     }
33417 });/*
33418  * Based on:
33419  * Ext JS Library 1.1.1
33420  * Copyright(c) 2006-2007, Ext JS, LLC.
33421  *
33422  * Originally Released Under LGPL - original licence link has changed is not relivant.
33423  *
33424  * Fork - LGPL
33425  * <script type="text/javascript">
33426  */
33427  
33428 /**
33429  * @class Roo.tree.AsyncTreeNode
33430  * @extends Roo.tree.TreeNode
33431  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33432  * @constructor
33433  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33434  */
33435  Roo.tree.AsyncTreeNode = function(config){
33436     this.loaded = false;
33437     this.loading = false;
33438     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33439     /**
33440     * @event beforeload
33441     * Fires before this node is loaded, return false to cancel
33442     * @param {Node} this This node
33443     */
33444     this.addEvents({'beforeload':true, 'load': true});
33445     /**
33446     * @event load
33447     * Fires when this node is loaded
33448     * @param {Node} this This node
33449     */
33450     /**
33451      * The loader used by this node (defaults to using the tree's defined loader)
33452      * @type TreeLoader
33453      * @property loader
33454      */
33455 };
33456 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33457     expand : function(deep, anim, callback){
33458         if(this.loading){ // if an async load is already running, waiting til it's done
33459             var timer;
33460             var f = function(){
33461                 if(!this.loading){ // done loading
33462                     clearInterval(timer);
33463                     this.expand(deep, anim, callback);
33464                 }
33465             }.createDelegate(this);
33466             timer = setInterval(f, 200);
33467             return;
33468         }
33469         if(!this.loaded){
33470             if(this.fireEvent("beforeload", this) === false){
33471                 return;
33472             }
33473             this.loading = true;
33474             this.ui.beforeLoad(this);
33475             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33476             if(loader){
33477                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33478                 return;
33479             }
33480         }
33481         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33482     },
33483     
33484     /**
33485      * Returns true if this node is currently loading
33486      * @return {Boolean}
33487      */
33488     isLoading : function(){
33489         return this.loading;  
33490     },
33491     
33492     loadComplete : function(deep, anim, callback){
33493         this.loading = false;
33494         this.loaded = true;
33495         this.ui.afterLoad(this);
33496         this.fireEvent("load", this);
33497         this.expand(deep, anim, callback);
33498     },
33499     
33500     /**
33501      * Returns true if this node has been loaded
33502      * @return {Boolean}
33503      */
33504     isLoaded : function(){
33505         return this.loaded;
33506     },
33507     
33508     hasChildNodes : function(){
33509         if(!this.isLeaf() && !this.loaded){
33510             return true;
33511         }else{
33512             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33513         }
33514     },
33515
33516     /**
33517      * Trigger a reload for this node
33518      * @param {Function} callback
33519      */
33520     reload : function(callback){
33521         this.collapse(false, false);
33522         while(this.firstChild){
33523             this.removeChild(this.firstChild);
33524         }
33525         this.childrenRendered = false;
33526         this.loaded = false;
33527         if(this.isHiddenRoot()){
33528             this.expanded = false;
33529         }
33530         this.expand(false, false, callback);
33531     }
33532 });/*
33533  * Based on:
33534  * Ext JS Library 1.1.1
33535  * Copyright(c) 2006-2007, Ext JS, LLC.
33536  *
33537  * Originally Released Under LGPL - original licence link has changed is not relivant.
33538  *
33539  * Fork - LGPL
33540  * <script type="text/javascript">
33541  */
33542  
33543 /**
33544  * @class Roo.tree.TreeNodeUI
33545  * @constructor
33546  * @param {Object} node The node to render
33547  * The TreeNode UI implementation is separate from the
33548  * tree implementation. Unless you are customizing the tree UI,
33549  * you should never have to use this directly.
33550  */
33551 Roo.tree.TreeNodeUI = function(node){
33552     this.node = node;
33553     this.rendered = false;
33554     this.animating = false;
33555     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33556 };
33557
33558 Roo.tree.TreeNodeUI.prototype = {
33559     removeChild : function(node){
33560         if(this.rendered){
33561             this.ctNode.removeChild(node.ui.getEl());
33562         }
33563     },
33564
33565     beforeLoad : function(){
33566          this.addClass("x-tree-node-loading");
33567     },
33568
33569     afterLoad : function(){
33570          this.removeClass("x-tree-node-loading");
33571     },
33572
33573     onTextChange : function(node, text, oldText){
33574         if(this.rendered){
33575             this.textNode.innerHTML = text;
33576         }
33577     },
33578
33579     onDisableChange : function(node, state){
33580         this.disabled = state;
33581         if(state){
33582             this.addClass("x-tree-node-disabled");
33583         }else{
33584             this.removeClass("x-tree-node-disabled");
33585         }
33586     },
33587
33588     onSelectedChange : function(state){
33589         if(state){
33590             this.focus();
33591             this.addClass("x-tree-selected");
33592         }else{
33593             //this.blur();
33594             this.removeClass("x-tree-selected");
33595         }
33596     },
33597
33598     onMove : function(tree, node, oldParent, newParent, index, refNode){
33599         this.childIndent = null;
33600         if(this.rendered){
33601             var targetNode = newParent.ui.getContainer();
33602             if(!targetNode){//target not rendered
33603                 this.holder = document.createElement("div");
33604                 this.holder.appendChild(this.wrap);
33605                 return;
33606             }
33607             var insertBefore = refNode ? refNode.ui.getEl() : null;
33608             if(insertBefore){
33609                 targetNode.insertBefore(this.wrap, insertBefore);
33610             }else{
33611                 targetNode.appendChild(this.wrap);
33612             }
33613             this.node.renderIndent(true);
33614         }
33615     },
33616
33617     addClass : function(cls){
33618         if(this.elNode){
33619             Roo.fly(this.elNode).addClass(cls);
33620         }
33621     },
33622
33623     removeClass : function(cls){
33624         if(this.elNode){
33625             Roo.fly(this.elNode).removeClass(cls);
33626         }
33627     },
33628
33629     remove : function(){
33630         if(this.rendered){
33631             this.holder = document.createElement("div");
33632             this.holder.appendChild(this.wrap);
33633         }
33634     },
33635
33636     fireEvent : function(){
33637         return this.node.fireEvent.apply(this.node, arguments);
33638     },
33639
33640     initEvents : function(){
33641         this.node.on("move", this.onMove, this);
33642         var E = Roo.EventManager;
33643         var a = this.anchor;
33644
33645         var el = Roo.fly(a, '_treeui');
33646
33647         if(Roo.isOpera){ // opera render bug ignores the CSS
33648             el.setStyle("text-decoration", "none");
33649         }
33650
33651         el.on("click", this.onClick, this);
33652         el.on("dblclick", this.onDblClick, this);
33653
33654         if(this.checkbox){
33655             Roo.EventManager.on(this.checkbox,
33656                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33657         }
33658
33659         el.on("contextmenu", this.onContextMenu, this);
33660
33661         var icon = Roo.fly(this.iconNode);
33662         icon.on("click", this.onClick, this);
33663         icon.on("dblclick", this.onDblClick, this);
33664         icon.on("contextmenu", this.onContextMenu, this);
33665         E.on(this.ecNode, "click", this.ecClick, this, true);
33666
33667         if(this.node.disabled){
33668             this.addClass("x-tree-node-disabled");
33669         }
33670         if(this.node.hidden){
33671             this.addClass("x-tree-node-disabled");
33672         }
33673         var ot = this.node.getOwnerTree();
33674         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33675         if(dd && (!this.node.isRoot || ot.rootVisible)){
33676             Roo.dd.Registry.register(this.elNode, {
33677                 node: this.node,
33678                 handles: this.getDDHandles(),
33679                 isHandle: false
33680             });
33681         }
33682     },
33683
33684     getDDHandles : function(){
33685         return [this.iconNode, this.textNode];
33686     },
33687
33688     hide : function(){
33689         if(this.rendered){
33690             this.wrap.style.display = "none";
33691         }
33692     },
33693
33694     show : function(){
33695         if(this.rendered){
33696             this.wrap.style.display = "";
33697         }
33698     },
33699
33700     onContextMenu : function(e){
33701         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33702             e.preventDefault();
33703             this.focus();
33704             this.fireEvent("contextmenu", this.node, e);
33705         }
33706     },
33707
33708     onClick : function(e){
33709         if(this.dropping){
33710             e.stopEvent();
33711             return;
33712         }
33713         if(this.fireEvent("beforeclick", this.node, e) !== false){
33714             if(!this.disabled && this.node.attributes.href){
33715                 this.fireEvent("click", this.node, e);
33716                 return;
33717             }
33718             e.preventDefault();
33719             if(this.disabled){
33720                 return;
33721             }
33722
33723             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33724                 this.node.toggle();
33725             }
33726
33727             this.fireEvent("click", this.node, e);
33728         }else{
33729             e.stopEvent();
33730         }
33731     },
33732
33733     onDblClick : function(e){
33734         e.preventDefault();
33735         if(this.disabled){
33736             return;
33737         }
33738         if(this.checkbox){
33739             this.toggleCheck();
33740         }
33741         if(!this.animating && this.node.hasChildNodes()){
33742             this.node.toggle();
33743         }
33744         this.fireEvent("dblclick", this.node, e);
33745     },
33746
33747     onCheckChange : function(){
33748         var checked = this.checkbox.checked;
33749         this.node.attributes.checked = checked;
33750         this.fireEvent('checkchange', this.node, checked);
33751     },
33752
33753     ecClick : function(e){
33754         if(!this.animating && this.node.hasChildNodes()){
33755             this.node.toggle();
33756         }
33757     },
33758
33759     startDrop : function(){
33760         this.dropping = true;
33761     },
33762
33763     // delayed drop so the click event doesn't get fired on a drop
33764     endDrop : function(){
33765        setTimeout(function(){
33766            this.dropping = false;
33767        }.createDelegate(this), 50);
33768     },
33769
33770     expand : function(){
33771         this.updateExpandIcon();
33772         this.ctNode.style.display = "";
33773     },
33774
33775     focus : function(){
33776         if(!this.node.preventHScroll){
33777             try{this.anchor.focus();
33778             }catch(e){}
33779         }else if(!Roo.isIE){
33780             try{
33781                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33782                 var l = noscroll.scrollLeft;
33783                 this.anchor.focus();
33784                 noscroll.scrollLeft = l;
33785             }catch(e){}
33786         }
33787     },
33788
33789     toggleCheck : function(value){
33790         var cb = this.checkbox;
33791         if(cb){
33792             cb.checked = (value === undefined ? !cb.checked : value);
33793         }
33794     },
33795
33796     blur : function(){
33797         try{
33798             this.anchor.blur();
33799         }catch(e){}
33800     },
33801
33802     animExpand : function(callback){
33803         var ct = Roo.get(this.ctNode);
33804         ct.stopFx();
33805         if(!this.node.hasChildNodes()){
33806             this.updateExpandIcon();
33807             this.ctNode.style.display = "";
33808             Roo.callback(callback);
33809             return;
33810         }
33811         this.animating = true;
33812         this.updateExpandIcon();
33813
33814         ct.slideIn('t', {
33815            callback : function(){
33816                this.animating = false;
33817                Roo.callback(callback);
33818             },
33819             scope: this,
33820             duration: this.node.ownerTree.duration || .25
33821         });
33822     },
33823
33824     highlight : function(){
33825         var tree = this.node.getOwnerTree();
33826         Roo.fly(this.wrap).highlight(
33827             tree.hlColor || "C3DAF9",
33828             {endColor: tree.hlBaseColor}
33829         );
33830     },
33831
33832     collapse : function(){
33833         this.updateExpandIcon();
33834         this.ctNode.style.display = "none";
33835     },
33836
33837     animCollapse : function(callback){
33838         var ct = Roo.get(this.ctNode);
33839         ct.enableDisplayMode('block');
33840         ct.stopFx();
33841
33842         this.animating = true;
33843         this.updateExpandIcon();
33844
33845         ct.slideOut('t', {
33846             callback : function(){
33847                this.animating = false;
33848                Roo.callback(callback);
33849             },
33850             scope: this,
33851             duration: this.node.ownerTree.duration || .25
33852         });
33853     },
33854
33855     getContainer : function(){
33856         return this.ctNode;
33857     },
33858
33859     getEl : function(){
33860         return this.wrap;
33861     },
33862
33863     appendDDGhost : function(ghostNode){
33864         ghostNode.appendChild(this.elNode.cloneNode(true));
33865     },
33866
33867     getDDRepairXY : function(){
33868         return Roo.lib.Dom.getXY(this.iconNode);
33869     },
33870
33871     onRender : function(){
33872         this.render();
33873     },
33874
33875     render : function(bulkRender){
33876         var n = this.node, a = n.attributes;
33877         var targetNode = n.parentNode ?
33878               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33879
33880         if(!this.rendered){
33881             this.rendered = true;
33882
33883             this.renderElements(n, a, targetNode, bulkRender);
33884
33885             if(a.qtip){
33886                if(this.textNode.setAttributeNS){
33887                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33888                    if(a.qtipTitle){
33889                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33890                    }
33891                }else{
33892                    this.textNode.setAttribute("ext:qtip", a.qtip);
33893                    if(a.qtipTitle){
33894                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33895                    }
33896                }
33897             }else if(a.qtipCfg){
33898                 a.qtipCfg.target = Roo.id(this.textNode);
33899                 Roo.QuickTips.register(a.qtipCfg);
33900             }
33901             this.initEvents();
33902             if(!this.node.expanded){
33903                 this.updateExpandIcon();
33904             }
33905         }else{
33906             if(bulkRender === true) {
33907                 targetNode.appendChild(this.wrap);
33908             }
33909         }
33910     },
33911
33912     renderElements : function(n, a, targetNode, bulkRender)
33913     {
33914         // add some indent caching, this helps performance when rendering a large tree
33915         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33916         var t = n.getOwnerTree();
33917         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33918         if (typeof(n.attributes.html) != 'undefined') {
33919             txt = n.attributes.html;
33920         }
33921         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33922         var cb = typeof a.checked == 'boolean';
33923         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33924         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33925             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33926             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33927             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33928             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33929             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33930              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33931                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33932             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33933             "</li>"];
33934
33935         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33936             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33937                                 n.nextSibling.ui.getEl(), buf.join(""));
33938         }else{
33939             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33940         }
33941
33942         this.elNode = this.wrap.childNodes[0];
33943         this.ctNode = this.wrap.childNodes[1];
33944         var cs = this.elNode.childNodes;
33945         this.indentNode = cs[0];
33946         this.ecNode = cs[1];
33947         this.iconNode = cs[2];
33948         var index = 3;
33949         if(cb){
33950             this.checkbox = cs[3];
33951             index++;
33952         }
33953         this.anchor = cs[index];
33954         this.textNode = cs[index].firstChild;
33955     },
33956
33957     getAnchor : function(){
33958         return this.anchor;
33959     },
33960
33961     getTextEl : function(){
33962         return this.textNode;
33963     },
33964
33965     getIconEl : function(){
33966         return this.iconNode;
33967     },
33968
33969     isChecked : function(){
33970         return this.checkbox ? this.checkbox.checked : false;
33971     },
33972
33973     updateExpandIcon : function(){
33974         if(this.rendered){
33975             var n = this.node, c1, c2;
33976             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33977             var hasChild = n.hasChildNodes();
33978             if(hasChild){
33979                 if(n.expanded){
33980                     cls += "-minus";
33981                     c1 = "x-tree-node-collapsed";
33982                     c2 = "x-tree-node-expanded";
33983                 }else{
33984                     cls += "-plus";
33985                     c1 = "x-tree-node-expanded";
33986                     c2 = "x-tree-node-collapsed";
33987                 }
33988                 if(this.wasLeaf){
33989                     this.removeClass("x-tree-node-leaf");
33990                     this.wasLeaf = false;
33991                 }
33992                 if(this.c1 != c1 || this.c2 != c2){
33993                     Roo.fly(this.elNode).replaceClass(c1, c2);
33994                     this.c1 = c1; this.c2 = c2;
33995                 }
33996             }else{
33997                 // this changes non-leafs into leafs if they have no children.
33998                 // it's not very rational behaviour..
33999                 
34000                 if(!this.wasLeaf && this.node.leaf){
34001                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34002                     delete this.c1;
34003                     delete this.c2;
34004                     this.wasLeaf = true;
34005                 }
34006             }
34007             var ecc = "x-tree-ec-icon "+cls;
34008             if(this.ecc != ecc){
34009                 this.ecNode.className = ecc;
34010                 this.ecc = ecc;
34011             }
34012         }
34013     },
34014
34015     getChildIndent : function(){
34016         if(!this.childIndent){
34017             var buf = [];
34018             var p = this.node;
34019             while(p){
34020                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34021                     if(!p.isLast()) {
34022                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34023                     } else {
34024                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34025                     }
34026                 }
34027                 p = p.parentNode;
34028             }
34029             this.childIndent = buf.join("");
34030         }
34031         return this.childIndent;
34032     },
34033
34034     renderIndent : function(){
34035         if(this.rendered){
34036             var indent = "";
34037             var p = this.node.parentNode;
34038             if(p){
34039                 indent = p.ui.getChildIndent();
34040             }
34041             if(this.indentMarkup != indent){ // don't rerender if not required
34042                 this.indentNode.innerHTML = indent;
34043                 this.indentMarkup = indent;
34044             }
34045             this.updateExpandIcon();
34046         }
34047     }
34048 };
34049
34050 Roo.tree.RootTreeNodeUI = function(){
34051     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34052 };
34053 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34054     render : function(){
34055         if(!this.rendered){
34056             var targetNode = this.node.ownerTree.innerCt.dom;
34057             this.node.expanded = true;
34058             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34059             this.wrap = this.ctNode = targetNode.firstChild;
34060         }
34061     },
34062     collapse : function(){
34063     },
34064     expand : function(){
34065     }
34066 });/*
34067  * Based on:
34068  * Ext JS Library 1.1.1
34069  * Copyright(c) 2006-2007, Ext JS, LLC.
34070  *
34071  * Originally Released Under LGPL - original licence link has changed is not relivant.
34072  *
34073  * Fork - LGPL
34074  * <script type="text/javascript">
34075  */
34076 /**
34077  * @class Roo.tree.TreeLoader
34078  * @extends Roo.util.Observable
34079  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34080  * nodes from a specified URL. The response must be a javascript Array definition
34081  * who's elements are node definition objects. eg:
34082  * <pre><code>
34083 {  success : true,
34084    data :      [
34085    
34086     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34087     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34088     ]
34089 }
34090
34091
34092 </code></pre>
34093  * <br><br>
34094  * The old style respose with just an array is still supported, but not recommended.
34095  * <br><br>
34096  *
34097  * A server request is sent, and child nodes are loaded only when a node is expanded.
34098  * The loading node's id is passed to the server under the parameter name "node" to
34099  * enable the server to produce the correct child nodes.
34100  * <br><br>
34101  * To pass extra parameters, an event handler may be attached to the "beforeload"
34102  * event, and the parameters specified in the TreeLoader's baseParams property:
34103  * <pre><code>
34104     myTreeLoader.on("beforeload", function(treeLoader, node) {
34105         this.baseParams.category = node.attributes.category;
34106     }, this);
34107 </code></pre><
34108  * This would pass an HTTP parameter called "category" to the server containing
34109  * the value of the Node's "category" attribute.
34110  * @constructor
34111  * Creates a new Treeloader.
34112  * @param {Object} config A config object containing config properties.
34113  */
34114 Roo.tree.TreeLoader = function(config){
34115     this.baseParams = {};
34116     this.requestMethod = "POST";
34117     Roo.apply(this, config);
34118
34119     this.addEvents({
34120     
34121         /**
34122          * @event beforeload
34123          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34124          * @param {Object} This TreeLoader object.
34125          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34126          * @param {Object} callback The callback function specified in the {@link #load} call.
34127          */
34128         beforeload : true,
34129         /**
34130          * @event load
34131          * Fires when the node has been successfuly loaded.
34132          * @param {Object} This TreeLoader object.
34133          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34134          * @param {Object} response The response object containing the data from the server.
34135          */
34136         load : true,
34137         /**
34138          * @event loadexception
34139          * Fires if the network request failed.
34140          * @param {Object} This TreeLoader object.
34141          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34142          * @param {Object} response The response object containing the data from the server.
34143          */
34144         loadexception : true,
34145         /**
34146          * @event create
34147          * Fires before a node is created, enabling you to return custom Node types 
34148          * @param {Object} This TreeLoader object.
34149          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34150          */
34151         create : true
34152     });
34153
34154     Roo.tree.TreeLoader.superclass.constructor.call(this);
34155 };
34156
34157 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34158     /**
34159     * @cfg {String} dataUrl The URL from which to request a Json string which
34160     * specifies an array of node definition object representing the child nodes
34161     * to be loaded.
34162     */
34163     /**
34164     * @cfg {String} requestMethod either GET or POST
34165     * defaults to POST (due to BC)
34166     * to be loaded.
34167     */
34168     /**
34169     * @cfg {Object} baseParams (optional) An object containing properties which
34170     * specify HTTP parameters to be passed to each request for child nodes.
34171     */
34172     /**
34173     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34174     * created by this loader. If the attributes sent by the server have an attribute in this object,
34175     * they take priority.
34176     */
34177     /**
34178     * @cfg {Object} uiProviders (optional) An object containing properties which
34179     * 
34180     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34181     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34182     * <i>uiProvider</i> attribute of a returned child node is a string rather
34183     * than a reference to a TreeNodeUI implementation, this that string value
34184     * is used as a property name in the uiProviders object. You can define the provider named
34185     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34186     */
34187     uiProviders : {},
34188
34189     /**
34190     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34191     * child nodes before loading.
34192     */
34193     clearOnLoad : true,
34194
34195     /**
34196     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34197     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34198     * Grid query { data : [ .....] }
34199     */
34200     
34201     root : false,
34202      /**
34203     * @cfg {String} queryParam (optional) 
34204     * Name of the query as it will be passed on the querystring (defaults to 'node')
34205     * eg. the request will be ?node=[id]
34206     */
34207     
34208     
34209     queryParam: false,
34210     
34211     /**
34212      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34213      * This is called automatically when a node is expanded, but may be used to reload
34214      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34215      * @param {Roo.tree.TreeNode} node
34216      * @param {Function} callback
34217      */
34218     load : function(node, callback){
34219         if(this.clearOnLoad){
34220             while(node.firstChild){
34221                 node.removeChild(node.firstChild);
34222             }
34223         }
34224         if(node.attributes.children){ // preloaded json children
34225             var cs = node.attributes.children;
34226             for(var i = 0, len = cs.length; i < len; i++){
34227                 node.appendChild(this.createNode(cs[i]));
34228             }
34229             if(typeof callback == "function"){
34230                 callback();
34231             }
34232         }else if(this.dataUrl){
34233             this.requestData(node, callback);
34234         }
34235     },
34236
34237     getParams: function(node){
34238         var buf = [], bp = this.baseParams;
34239         for(var key in bp){
34240             if(typeof bp[key] != "function"){
34241                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34242             }
34243         }
34244         var n = this.queryParam === false ? 'node' : this.queryParam;
34245         buf.push(n + "=", encodeURIComponent(node.id));
34246         return buf.join("");
34247     },
34248
34249     requestData : function(node, callback){
34250         if(this.fireEvent("beforeload", this, node, callback) !== false){
34251             this.transId = Roo.Ajax.request({
34252                 method:this.requestMethod,
34253                 url: this.dataUrl||this.url,
34254                 success: this.handleResponse,
34255                 failure: this.handleFailure,
34256                 scope: this,
34257                 argument: {callback: callback, node: node},
34258                 params: this.getParams(node)
34259             });
34260         }else{
34261             // if the load is cancelled, make sure we notify
34262             // the node that we are done
34263             if(typeof callback == "function"){
34264                 callback();
34265             }
34266         }
34267     },
34268
34269     isLoading : function(){
34270         return this.transId ? true : false;
34271     },
34272
34273     abort : function(){
34274         if(this.isLoading()){
34275             Roo.Ajax.abort(this.transId);
34276         }
34277     },
34278
34279     // private
34280     createNode : function(attr)
34281     {
34282         // apply baseAttrs, nice idea Corey!
34283         if(this.baseAttrs){
34284             Roo.applyIf(attr, this.baseAttrs);
34285         }
34286         if(this.applyLoader !== false){
34287             attr.loader = this;
34288         }
34289         // uiProvider = depreciated..
34290         
34291         if(typeof(attr.uiProvider) == 'string'){
34292            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34293                 /**  eval:var:attr */ eval(attr.uiProvider);
34294         }
34295         if(typeof(this.uiProviders['default']) != 'undefined') {
34296             attr.uiProvider = this.uiProviders['default'];
34297         }
34298         
34299         this.fireEvent('create', this, attr);
34300         
34301         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34302         return(attr.leaf ?
34303                         new Roo.tree.TreeNode(attr) :
34304                         new Roo.tree.AsyncTreeNode(attr));
34305     },
34306
34307     processResponse : function(response, node, callback)
34308     {
34309         var json = response.responseText;
34310         try {
34311             
34312             var o = Roo.decode(json);
34313             
34314             if (this.root === false && typeof(o.success) != undefined) {
34315                 this.root = 'data'; // the default behaviour for list like data..
34316                 }
34317                 
34318             if (this.root !== false &&  !o.success) {
34319                 // it's a failure condition.
34320                 var a = response.argument;
34321                 this.fireEvent("loadexception", this, a.node, response);
34322                 Roo.log("Load failed - should have a handler really");
34323                 return;
34324             }
34325             
34326             
34327             
34328             if (this.root !== false) {
34329                  o = o[this.root];
34330             }
34331             
34332             for(var i = 0, len = o.length; i < len; i++){
34333                 var n = this.createNode(o[i]);
34334                 if(n){
34335                     node.appendChild(n);
34336                 }
34337             }
34338             if(typeof callback == "function"){
34339                 callback(this, node);
34340             }
34341         }catch(e){
34342             this.handleFailure(response);
34343         }
34344     },
34345
34346     handleResponse : function(response){
34347         this.transId = false;
34348         var a = response.argument;
34349         this.processResponse(response, a.node, a.callback);
34350         this.fireEvent("load", this, a.node, response);
34351     },
34352
34353     handleFailure : function(response)
34354     {
34355         // should handle failure better..
34356         this.transId = false;
34357         var a = response.argument;
34358         this.fireEvent("loadexception", this, a.node, response);
34359         if(typeof a.callback == "function"){
34360             a.callback(this, a.node);
34361         }
34362     }
34363 });/*
34364  * Based on:
34365  * Ext JS Library 1.1.1
34366  * Copyright(c) 2006-2007, Ext JS, LLC.
34367  *
34368  * Originally Released Under LGPL - original licence link has changed is not relivant.
34369  *
34370  * Fork - LGPL
34371  * <script type="text/javascript">
34372  */
34373
34374 /**
34375 * @class Roo.tree.TreeFilter
34376 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34377 * @param {TreePanel} tree
34378 * @param {Object} config (optional)
34379  */
34380 Roo.tree.TreeFilter = function(tree, config){
34381     this.tree = tree;
34382     this.filtered = {};
34383     Roo.apply(this, config);
34384 };
34385
34386 Roo.tree.TreeFilter.prototype = {
34387     clearBlank:false,
34388     reverse:false,
34389     autoClear:false,
34390     remove:false,
34391
34392      /**
34393      * Filter the data by a specific attribute.
34394      * @param {String/RegExp} value Either string that the attribute value
34395      * should start with or a RegExp to test against the attribute
34396      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34397      * @param {TreeNode} startNode (optional) The node to start the filter at.
34398      */
34399     filter : function(value, attr, startNode){
34400         attr = attr || "text";
34401         var f;
34402         if(typeof value == "string"){
34403             var vlen = value.length;
34404             // auto clear empty filter
34405             if(vlen == 0 && this.clearBlank){
34406                 this.clear();
34407                 return;
34408             }
34409             value = value.toLowerCase();
34410             f = function(n){
34411                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34412             };
34413         }else if(value.exec){ // regex?
34414             f = function(n){
34415                 return value.test(n.attributes[attr]);
34416             };
34417         }else{
34418             throw 'Illegal filter type, must be string or regex';
34419         }
34420         this.filterBy(f, null, startNode);
34421         },
34422
34423     /**
34424      * Filter by a function. The passed function will be called with each
34425      * node in the tree (or from the startNode). If the function returns true, the node is kept
34426      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34427      * @param {Function} fn The filter function
34428      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34429      */
34430     filterBy : function(fn, scope, startNode){
34431         startNode = startNode || this.tree.root;
34432         if(this.autoClear){
34433             this.clear();
34434         }
34435         var af = this.filtered, rv = this.reverse;
34436         var f = function(n){
34437             if(n == startNode){
34438                 return true;
34439             }
34440             if(af[n.id]){
34441                 return false;
34442             }
34443             var m = fn.call(scope || n, n);
34444             if(!m || rv){
34445                 af[n.id] = n;
34446                 n.ui.hide();
34447                 return false;
34448             }
34449             return true;
34450         };
34451         startNode.cascade(f);
34452         if(this.remove){
34453            for(var id in af){
34454                if(typeof id != "function"){
34455                    var n = af[id];
34456                    if(n && n.parentNode){
34457                        n.parentNode.removeChild(n);
34458                    }
34459                }
34460            }
34461         }
34462     },
34463
34464     /**
34465      * Clears the current filter. Note: with the "remove" option
34466      * set a filter cannot be cleared.
34467      */
34468     clear : function(){
34469         var t = this.tree;
34470         var af = this.filtered;
34471         for(var id in af){
34472             if(typeof id != "function"){
34473                 var n = af[id];
34474                 if(n){
34475                     n.ui.show();
34476                 }
34477             }
34478         }
34479         this.filtered = {};
34480     }
34481 };
34482 /*
34483  * Based on:
34484  * Ext JS Library 1.1.1
34485  * Copyright(c) 2006-2007, Ext JS, LLC.
34486  *
34487  * Originally Released Under LGPL - original licence link has changed is not relivant.
34488  *
34489  * Fork - LGPL
34490  * <script type="text/javascript">
34491  */
34492  
34493
34494 /**
34495  * @class Roo.tree.TreeSorter
34496  * Provides sorting of nodes in a TreePanel
34497  * 
34498  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34499  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34500  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34501  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34502  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34503  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34504  * @constructor
34505  * @param {TreePanel} tree
34506  * @param {Object} config
34507  */
34508 Roo.tree.TreeSorter = function(tree, config){
34509     Roo.apply(this, config);
34510     tree.on("beforechildrenrendered", this.doSort, this);
34511     tree.on("append", this.updateSort, this);
34512     tree.on("insert", this.updateSort, this);
34513     
34514     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34515     var p = this.property || "text";
34516     var sortType = this.sortType;
34517     var fs = this.folderSort;
34518     var cs = this.caseSensitive === true;
34519     var leafAttr = this.leafAttr || 'leaf';
34520
34521     this.sortFn = function(n1, n2){
34522         if(fs){
34523             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34524                 return 1;
34525             }
34526             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34527                 return -1;
34528             }
34529         }
34530         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34531         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34532         if(v1 < v2){
34533                         return dsc ? +1 : -1;
34534                 }else if(v1 > v2){
34535                         return dsc ? -1 : +1;
34536         }else{
34537                 return 0;
34538         }
34539     };
34540 };
34541
34542 Roo.tree.TreeSorter.prototype = {
34543     doSort : function(node){
34544         node.sort(this.sortFn);
34545     },
34546     
34547     compareNodes : function(n1, n2){
34548         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34549     },
34550     
34551     updateSort : function(tree, node){
34552         if(node.childrenRendered){
34553             this.doSort.defer(1, this, [node]);
34554         }
34555     }
34556 };/*
34557  * Based on:
34558  * Ext JS Library 1.1.1
34559  * Copyright(c) 2006-2007, Ext JS, LLC.
34560  *
34561  * Originally Released Under LGPL - original licence link has changed is not relivant.
34562  *
34563  * Fork - LGPL
34564  * <script type="text/javascript">
34565  */
34566
34567 if(Roo.dd.DropZone){
34568     
34569 Roo.tree.TreeDropZone = function(tree, config){
34570     this.allowParentInsert = false;
34571     this.allowContainerDrop = false;
34572     this.appendOnly = false;
34573     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34574     this.tree = tree;
34575     this.lastInsertClass = "x-tree-no-status";
34576     this.dragOverData = {};
34577 };
34578
34579 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34580     ddGroup : "TreeDD",
34581     scroll:  true,
34582     
34583     expandDelay : 1000,
34584     
34585     expandNode : function(node){
34586         if(node.hasChildNodes() && !node.isExpanded()){
34587             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34588         }
34589     },
34590     
34591     queueExpand : function(node){
34592         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34593     },
34594     
34595     cancelExpand : function(){
34596         if(this.expandProcId){
34597             clearTimeout(this.expandProcId);
34598             this.expandProcId = false;
34599         }
34600     },
34601     
34602     isValidDropPoint : function(n, pt, dd, e, data){
34603         if(!n || !data){ return false; }
34604         var targetNode = n.node;
34605         var dropNode = data.node;
34606         // default drop rules
34607         if(!(targetNode && targetNode.isTarget && pt)){
34608             return false;
34609         }
34610         if(pt == "append" && targetNode.allowChildren === false){
34611             return false;
34612         }
34613         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34614             return false;
34615         }
34616         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34617             return false;
34618         }
34619         // reuse the object
34620         var overEvent = this.dragOverData;
34621         overEvent.tree = this.tree;
34622         overEvent.target = targetNode;
34623         overEvent.data = data;
34624         overEvent.point = pt;
34625         overEvent.source = dd;
34626         overEvent.rawEvent = e;
34627         overEvent.dropNode = dropNode;
34628         overEvent.cancel = false;  
34629         var result = this.tree.fireEvent("nodedragover", overEvent);
34630         return overEvent.cancel === false && result !== false;
34631     },
34632     
34633     getDropPoint : function(e, n, dd)
34634     {
34635         var tn = n.node;
34636         if(tn.isRoot){
34637             return tn.allowChildren !== false ? "append" : false; // always append for root
34638         }
34639         var dragEl = n.ddel;
34640         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34641         var y = Roo.lib.Event.getPageY(e);
34642         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34643         
34644         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34645         var noAppend = tn.allowChildren === false;
34646         if(this.appendOnly || tn.parentNode.allowChildren === false){
34647             return noAppend ? false : "append";
34648         }
34649         var noBelow = false;
34650         if(!this.allowParentInsert){
34651             noBelow = tn.hasChildNodes() && tn.isExpanded();
34652         }
34653         var q = (b - t) / (noAppend ? 2 : 3);
34654         if(y >= t && y < (t + q)){
34655             return "above";
34656         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34657             return "below";
34658         }else{
34659             return "append";
34660         }
34661     },
34662     
34663     onNodeEnter : function(n, dd, e, data)
34664     {
34665         this.cancelExpand();
34666     },
34667     
34668     onNodeOver : function(n, dd, e, data)
34669     {
34670        
34671         var pt = this.getDropPoint(e, n, dd);
34672         var node = n.node;
34673         
34674         // auto node expand check
34675         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34676             this.queueExpand(node);
34677         }else if(pt != "append"){
34678             this.cancelExpand();
34679         }
34680         
34681         // set the insert point style on the target node
34682         var returnCls = this.dropNotAllowed;
34683         if(this.isValidDropPoint(n, pt, dd, e, data)){
34684            if(pt){
34685                var el = n.ddel;
34686                var cls;
34687                if(pt == "above"){
34688                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34689                    cls = "x-tree-drag-insert-above";
34690                }else if(pt == "below"){
34691                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34692                    cls = "x-tree-drag-insert-below";
34693                }else{
34694                    returnCls = "x-tree-drop-ok-append";
34695                    cls = "x-tree-drag-append";
34696                }
34697                if(this.lastInsertClass != cls){
34698                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34699                    this.lastInsertClass = cls;
34700                }
34701            }
34702        }
34703        return returnCls;
34704     },
34705     
34706     onNodeOut : function(n, dd, e, data){
34707         
34708         this.cancelExpand();
34709         this.removeDropIndicators(n);
34710     },
34711     
34712     onNodeDrop : function(n, dd, e, data){
34713         var point = this.getDropPoint(e, n, dd);
34714         var targetNode = n.node;
34715         targetNode.ui.startDrop();
34716         if(!this.isValidDropPoint(n, point, dd, e, data)){
34717             targetNode.ui.endDrop();
34718             return false;
34719         }
34720         // first try to find the drop node
34721         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34722         var dropEvent = {
34723             tree : this.tree,
34724             target: targetNode,
34725             data: data,
34726             point: point,
34727             source: dd,
34728             rawEvent: e,
34729             dropNode: dropNode,
34730             cancel: !dropNode   
34731         };
34732         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34733         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34734             targetNode.ui.endDrop();
34735             return false;
34736         }
34737         // allow target changing
34738         targetNode = dropEvent.target;
34739         if(point == "append" && !targetNode.isExpanded()){
34740             targetNode.expand(false, null, function(){
34741                 this.completeDrop(dropEvent);
34742             }.createDelegate(this));
34743         }else{
34744             this.completeDrop(dropEvent);
34745         }
34746         return true;
34747     },
34748     
34749     completeDrop : function(de){
34750         var ns = de.dropNode, p = de.point, t = de.target;
34751         if(!(ns instanceof Array)){
34752             ns = [ns];
34753         }
34754         var n;
34755         for(var i = 0, len = ns.length; i < len; i++){
34756             n = ns[i];
34757             if(p == "above"){
34758                 t.parentNode.insertBefore(n, t);
34759             }else if(p == "below"){
34760                 t.parentNode.insertBefore(n, t.nextSibling);
34761             }else{
34762                 t.appendChild(n);
34763             }
34764         }
34765         n.ui.focus();
34766         if(this.tree.hlDrop){
34767             n.ui.highlight();
34768         }
34769         t.ui.endDrop();
34770         this.tree.fireEvent("nodedrop", de);
34771     },
34772     
34773     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34774         if(this.tree.hlDrop){
34775             dropNode.ui.focus();
34776             dropNode.ui.highlight();
34777         }
34778         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34779     },
34780     
34781     getTree : function(){
34782         return this.tree;
34783     },
34784     
34785     removeDropIndicators : function(n){
34786         if(n && n.ddel){
34787             var el = n.ddel;
34788             Roo.fly(el).removeClass([
34789                     "x-tree-drag-insert-above",
34790                     "x-tree-drag-insert-below",
34791                     "x-tree-drag-append"]);
34792             this.lastInsertClass = "_noclass";
34793         }
34794     },
34795     
34796     beforeDragDrop : function(target, e, id){
34797         this.cancelExpand();
34798         return true;
34799     },
34800     
34801     afterRepair : function(data){
34802         if(data && Roo.enableFx){
34803             data.node.ui.highlight();
34804         }
34805         this.hideProxy();
34806     } 
34807     
34808 });
34809
34810 }
34811 /*
34812  * Based on:
34813  * Ext JS Library 1.1.1
34814  * Copyright(c) 2006-2007, Ext JS, LLC.
34815  *
34816  * Originally Released Under LGPL - original licence link has changed is not relivant.
34817  *
34818  * Fork - LGPL
34819  * <script type="text/javascript">
34820  */
34821  
34822
34823 if(Roo.dd.DragZone){
34824 Roo.tree.TreeDragZone = function(tree, config){
34825     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34826     this.tree = tree;
34827 };
34828
34829 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34830     ddGroup : "TreeDD",
34831    
34832     onBeforeDrag : function(data, e){
34833         var n = data.node;
34834         return n && n.draggable && !n.disabled;
34835     },
34836      
34837     
34838     onInitDrag : function(e){
34839         var data = this.dragData;
34840         this.tree.getSelectionModel().select(data.node);
34841         this.proxy.update("");
34842         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34843         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34844     },
34845     
34846     getRepairXY : function(e, data){
34847         return data.node.ui.getDDRepairXY();
34848     },
34849     
34850     onEndDrag : function(data, e){
34851         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34852         
34853         
34854     },
34855     
34856     onValidDrop : function(dd, e, id){
34857         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34858         this.hideProxy();
34859     },
34860     
34861     beforeInvalidDrop : function(e, id){
34862         // this scrolls the original position back into view
34863         var sm = this.tree.getSelectionModel();
34864         sm.clearSelections();
34865         sm.select(this.dragData.node);
34866     }
34867 });
34868 }/*
34869  * Based on:
34870  * Ext JS Library 1.1.1
34871  * Copyright(c) 2006-2007, Ext JS, LLC.
34872  *
34873  * Originally Released Under LGPL - original licence link has changed is not relivant.
34874  *
34875  * Fork - LGPL
34876  * <script type="text/javascript">
34877  */
34878 /**
34879  * @class Roo.tree.TreeEditor
34880  * @extends Roo.Editor
34881  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34882  * as the editor field.
34883  * @constructor
34884  * @param {Object} config (used to be the tree panel.)
34885  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34886  * 
34887  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34888  * @cfg {Roo.form.TextField|Object} field The field configuration
34889  *
34890  * 
34891  */
34892 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34893     var tree = config;
34894     var field;
34895     if (oldconfig) { // old style..
34896         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34897     } else {
34898         // new style..
34899         tree = config.tree;
34900         config.field = config.field  || {};
34901         config.field.xtype = 'TextField';
34902         field = Roo.factory(config.field, Roo.form);
34903     }
34904     config = config || {};
34905     
34906     
34907     this.addEvents({
34908         /**
34909          * @event beforenodeedit
34910          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34911          * false from the handler of this event.
34912          * @param {Editor} this
34913          * @param {Roo.tree.Node} node 
34914          */
34915         "beforenodeedit" : true
34916     });
34917     
34918     //Roo.log(config);
34919     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34920
34921     this.tree = tree;
34922
34923     tree.on('beforeclick', this.beforeNodeClick, this);
34924     tree.getTreeEl().on('mousedown', this.hide, this);
34925     this.on('complete', this.updateNode, this);
34926     this.on('beforestartedit', this.fitToTree, this);
34927     this.on('startedit', this.bindScroll, this, {delay:10});
34928     this.on('specialkey', this.onSpecialKey, this);
34929 };
34930
34931 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34932     /**
34933      * @cfg {String} alignment
34934      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34935      */
34936     alignment: "l-l",
34937     // inherit
34938     autoSize: false,
34939     /**
34940      * @cfg {Boolean} hideEl
34941      * True to hide the bound element while the editor is displayed (defaults to false)
34942      */
34943     hideEl : false,
34944     /**
34945      * @cfg {String} cls
34946      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34947      */
34948     cls: "x-small-editor x-tree-editor",
34949     /**
34950      * @cfg {Boolean} shim
34951      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34952      */
34953     shim:false,
34954     // inherit
34955     shadow:"frame",
34956     /**
34957      * @cfg {Number} maxWidth
34958      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34959      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34960      * scroll and client offsets into account prior to each edit.
34961      */
34962     maxWidth: 250,
34963
34964     editDelay : 350,
34965
34966     // private
34967     fitToTree : function(ed, el){
34968         var td = this.tree.getTreeEl().dom, nd = el.dom;
34969         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34970             td.scrollLeft = nd.offsetLeft;
34971         }
34972         var w = Math.min(
34973                 this.maxWidth,
34974                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34975         this.setSize(w, '');
34976         
34977         return this.fireEvent('beforenodeedit', this, this.editNode);
34978         
34979     },
34980
34981     // private
34982     triggerEdit : function(node){
34983         this.completeEdit();
34984         this.editNode = node;
34985         this.startEdit(node.ui.textNode, node.text);
34986     },
34987
34988     // private
34989     bindScroll : function(){
34990         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34991     },
34992
34993     // private
34994     beforeNodeClick : function(node, e){
34995         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34996         this.lastClick = new Date();
34997         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34998             e.stopEvent();
34999             this.triggerEdit(node);
35000             return false;
35001         }
35002         return true;
35003     },
35004
35005     // private
35006     updateNode : function(ed, value){
35007         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35008         this.editNode.setText(value);
35009     },
35010
35011     // private
35012     onHide : function(){
35013         Roo.tree.TreeEditor.superclass.onHide.call(this);
35014         if(this.editNode){
35015             this.editNode.ui.focus();
35016         }
35017     },
35018
35019     // private
35020     onSpecialKey : function(field, e){
35021         var k = e.getKey();
35022         if(k == e.ESC){
35023             e.stopEvent();
35024             this.cancelEdit();
35025         }else if(k == e.ENTER && !e.hasModifier()){
35026             e.stopEvent();
35027             this.completeEdit();
35028         }
35029     }
35030 });//<Script type="text/javascript">
35031 /*
35032  * Based on:
35033  * Ext JS Library 1.1.1
35034  * Copyright(c) 2006-2007, Ext JS, LLC.
35035  *
35036  * Originally Released Under LGPL - original licence link has changed is not relivant.
35037  *
35038  * Fork - LGPL
35039  * <script type="text/javascript">
35040  */
35041  
35042 /**
35043  * Not documented??? - probably should be...
35044  */
35045
35046 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35047     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35048     
35049     renderElements : function(n, a, targetNode, bulkRender){
35050         //consel.log("renderElements?");
35051         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35052
35053         var t = n.getOwnerTree();
35054         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35055         
35056         var cols = t.columns;
35057         var bw = t.borderWidth;
35058         var c = cols[0];
35059         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35060          var cb = typeof a.checked == "boolean";
35061         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35062         var colcls = 'x-t-' + tid + '-c0';
35063         var buf = [
35064             '<li class="x-tree-node">',
35065             
35066                 
35067                 '<div class="x-tree-node-el ', a.cls,'">',
35068                     // extran...
35069                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35070                 
35071                 
35072                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35073                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35074                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35075                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35076                            (a.iconCls ? ' '+a.iconCls : ''),
35077                            '" unselectable="on" />',
35078                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35079                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35080                              
35081                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35082                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35083                             '<span unselectable="on" qtip="' + tx + '">',
35084                              tx,
35085                              '</span></a>' ,
35086                     '</div>',
35087                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35088                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35089                  ];
35090         for(var i = 1, len = cols.length; i < len; i++){
35091             c = cols[i];
35092             colcls = 'x-t-' + tid + '-c' +i;
35093             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35094             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35095                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35096                       "</div>");
35097          }
35098          
35099          buf.push(
35100             '</a>',
35101             '<div class="x-clear"></div></div>',
35102             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35103             "</li>");
35104         
35105         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35106             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35107                                 n.nextSibling.ui.getEl(), buf.join(""));
35108         }else{
35109             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35110         }
35111         var el = this.wrap.firstChild;
35112         this.elRow = el;
35113         this.elNode = el.firstChild;
35114         this.ranchor = el.childNodes[1];
35115         this.ctNode = this.wrap.childNodes[1];
35116         var cs = el.firstChild.childNodes;
35117         this.indentNode = cs[0];
35118         this.ecNode = cs[1];
35119         this.iconNode = cs[2];
35120         var index = 3;
35121         if(cb){
35122             this.checkbox = cs[3];
35123             index++;
35124         }
35125         this.anchor = cs[index];
35126         
35127         this.textNode = cs[index].firstChild;
35128         
35129         //el.on("click", this.onClick, this);
35130         //el.on("dblclick", this.onDblClick, this);
35131         
35132         
35133        // console.log(this);
35134     },
35135     initEvents : function(){
35136         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35137         
35138             
35139         var a = this.ranchor;
35140
35141         var el = Roo.get(a);
35142
35143         if(Roo.isOpera){ // opera render bug ignores the CSS
35144             el.setStyle("text-decoration", "none");
35145         }
35146
35147         el.on("click", this.onClick, this);
35148         el.on("dblclick", this.onDblClick, this);
35149         el.on("contextmenu", this.onContextMenu, this);
35150         
35151     },
35152     
35153     /*onSelectedChange : function(state){
35154         if(state){
35155             this.focus();
35156             this.addClass("x-tree-selected");
35157         }else{
35158             //this.blur();
35159             this.removeClass("x-tree-selected");
35160         }
35161     },*/
35162     addClass : function(cls){
35163         if(this.elRow){
35164             Roo.fly(this.elRow).addClass(cls);
35165         }
35166         
35167     },
35168     
35169     
35170     removeClass : function(cls){
35171         if(this.elRow){
35172             Roo.fly(this.elRow).removeClass(cls);
35173         }
35174     }
35175
35176     
35177     
35178 });//<Script type="text/javascript">
35179
35180 /*
35181  * Based on:
35182  * Ext JS Library 1.1.1
35183  * Copyright(c) 2006-2007, Ext JS, LLC.
35184  *
35185  * Originally Released Under LGPL - original licence link has changed is not relivant.
35186  *
35187  * Fork - LGPL
35188  * <script type="text/javascript">
35189  */
35190  
35191
35192 /**
35193  * @class Roo.tree.ColumnTree
35194  * @extends Roo.data.TreePanel
35195  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35196  * @cfg {int} borderWidth  compined right/left border allowance
35197  * @constructor
35198  * @param {String/HTMLElement/Element} el The container element
35199  * @param {Object} config
35200  */
35201 Roo.tree.ColumnTree =  function(el, config)
35202 {
35203    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35204    this.addEvents({
35205         /**
35206         * @event resize
35207         * Fire this event on a container when it resizes
35208         * @param {int} w Width
35209         * @param {int} h Height
35210         */
35211        "resize" : true
35212     });
35213     this.on('resize', this.onResize, this);
35214 };
35215
35216 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35217     //lines:false,
35218     
35219     
35220     borderWidth: Roo.isBorderBox ? 0 : 2, 
35221     headEls : false,
35222     
35223     render : function(){
35224         // add the header.....
35225        
35226         Roo.tree.ColumnTree.superclass.render.apply(this);
35227         
35228         this.el.addClass('x-column-tree');
35229         
35230         this.headers = this.el.createChild(
35231             {cls:'x-tree-headers'},this.innerCt.dom);
35232    
35233         var cols = this.columns, c;
35234         var totalWidth = 0;
35235         this.headEls = [];
35236         var  len = cols.length;
35237         for(var i = 0; i < len; i++){
35238              c = cols[i];
35239              totalWidth += c.width;
35240             this.headEls.push(this.headers.createChild({
35241                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35242                  cn: {
35243                      cls:'x-tree-hd-text',
35244                      html: c.header
35245                  },
35246                  style:'width:'+(c.width-this.borderWidth)+'px;'
35247              }));
35248         }
35249         this.headers.createChild({cls:'x-clear'});
35250         // prevent floats from wrapping when clipped
35251         this.headers.setWidth(totalWidth);
35252         //this.innerCt.setWidth(totalWidth);
35253         this.innerCt.setStyle({ overflow: 'auto' });
35254         this.onResize(this.width, this.height);
35255              
35256         
35257     },
35258     onResize : function(w,h)
35259     {
35260         this.height = h;
35261         this.width = w;
35262         // resize cols..
35263         this.innerCt.setWidth(this.width);
35264         this.innerCt.setHeight(this.height-20);
35265         
35266         // headers...
35267         var cols = this.columns, c;
35268         var totalWidth = 0;
35269         var expEl = false;
35270         var len = cols.length;
35271         for(var i = 0; i < len; i++){
35272             c = cols[i];
35273             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35274                 // it's the expander..
35275                 expEl  = this.headEls[i];
35276                 continue;
35277             }
35278             totalWidth += c.width;
35279             
35280         }
35281         if (expEl) {
35282             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35283         }
35284         this.headers.setWidth(w-20);
35285
35286         
35287         
35288         
35289     }
35290 });
35291 /*
35292  * Based on:
35293  * Ext JS Library 1.1.1
35294  * Copyright(c) 2006-2007, Ext JS, LLC.
35295  *
35296  * Originally Released Under LGPL - original licence link has changed is not relivant.
35297  *
35298  * Fork - LGPL
35299  * <script type="text/javascript">
35300  */
35301  
35302 /**
35303  * @class Roo.menu.Menu
35304  * @extends Roo.util.Observable
35305  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35306  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35307  * @constructor
35308  * Creates a new Menu
35309  * @param {Object} config Configuration options
35310  */
35311 Roo.menu.Menu = function(config){
35312     Roo.apply(this, config);
35313     this.id = this.id || Roo.id();
35314     this.addEvents({
35315         /**
35316          * @event beforeshow
35317          * Fires before this menu is displayed
35318          * @param {Roo.menu.Menu} this
35319          */
35320         beforeshow : true,
35321         /**
35322          * @event beforehide
35323          * Fires before this menu is hidden
35324          * @param {Roo.menu.Menu} this
35325          */
35326         beforehide : true,
35327         /**
35328          * @event show
35329          * Fires after this menu is displayed
35330          * @param {Roo.menu.Menu} this
35331          */
35332         show : true,
35333         /**
35334          * @event hide
35335          * Fires after this menu is hidden
35336          * @param {Roo.menu.Menu} this
35337          */
35338         hide : true,
35339         /**
35340          * @event click
35341          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35342          * @param {Roo.menu.Menu} this
35343          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35344          * @param {Roo.EventObject} e
35345          */
35346         click : true,
35347         /**
35348          * @event mouseover
35349          * Fires when the mouse is hovering over this menu
35350          * @param {Roo.menu.Menu} this
35351          * @param {Roo.EventObject} e
35352          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35353          */
35354         mouseover : true,
35355         /**
35356          * @event mouseout
35357          * Fires when the mouse exits this menu
35358          * @param {Roo.menu.Menu} this
35359          * @param {Roo.EventObject} e
35360          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35361          */
35362         mouseout : true,
35363         /**
35364          * @event itemclick
35365          * Fires when a menu item contained in this menu is clicked
35366          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35367          * @param {Roo.EventObject} e
35368          */
35369         itemclick: true
35370     });
35371     if (this.registerMenu) {
35372         Roo.menu.MenuMgr.register(this);
35373     }
35374     
35375     var mis = this.items;
35376     this.items = new Roo.util.MixedCollection();
35377     if(mis){
35378         this.add.apply(this, mis);
35379     }
35380 };
35381
35382 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35383     /**
35384      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35385      */
35386     minWidth : 120,
35387     /**
35388      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35389      * for bottom-right shadow (defaults to "sides")
35390      */
35391     shadow : "sides",
35392     /**
35393      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35394      * this menu (defaults to "tl-tr?")
35395      */
35396     subMenuAlign : "tl-tr?",
35397     /**
35398      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35399      * relative to its element of origin (defaults to "tl-bl?")
35400      */
35401     defaultAlign : "tl-bl?",
35402     /**
35403      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35404      */
35405     allowOtherMenus : false,
35406     /**
35407      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35408      */
35409     registerMenu : true,
35410
35411     hidden:true,
35412
35413     // private
35414     render : function(){
35415         if(this.el){
35416             return;
35417         }
35418         var el = this.el = new Roo.Layer({
35419             cls: "x-menu",
35420             shadow:this.shadow,
35421             constrain: false,
35422             parentEl: this.parentEl || document.body,
35423             zindex:15000
35424         });
35425
35426         this.keyNav = new Roo.menu.MenuNav(this);
35427
35428         if(this.plain){
35429             el.addClass("x-menu-plain");
35430         }
35431         if(this.cls){
35432             el.addClass(this.cls);
35433         }
35434         // generic focus element
35435         this.focusEl = el.createChild({
35436             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35437         });
35438         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35439         ul.on("click", this.onClick, this);
35440         ul.on("mouseover", this.onMouseOver, this);
35441         ul.on("mouseout", this.onMouseOut, this);
35442         this.items.each(function(item){
35443             if (item.hidden) {
35444                 return;
35445             }
35446             
35447             var li = document.createElement("li");
35448             li.className = "x-menu-list-item";
35449             ul.dom.appendChild(li);
35450             item.render(li, this);
35451         }, this);
35452         this.ul = ul;
35453         this.autoWidth();
35454     },
35455
35456     // private
35457     autoWidth : function(){
35458         var el = this.el, ul = this.ul;
35459         if(!el){
35460             return;
35461         }
35462         var w = this.width;
35463         if(w){
35464             el.setWidth(w);
35465         }else if(Roo.isIE){
35466             el.setWidth(this.minWidth);
35467             var t = el.dom.offsetWidth; // force recalc
35468             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35469         }
35470     },
35471
35472     // private
35473     delayAutoWidth : function(){
35474         if(this.rendered){
35475             if(!this.awTask){
35476                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35477             }
35478             this.awTask.delay(20);
35479         }
35480     },
35481
35482     // private
35483     findTargetItem : function(e){
35484         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35485         if(t && t.menuItemId){
35486             return this.items.get(t.menuItemId);
35487         }
35488     },
35489
35490     // private
35491     onClick : function(e){
35492         var t;
35493         if(t = this.findTargetItem(e)){
35494             t.onClick(e);
35495             this.fireEvent("click", this, t, e);
35496         }
35497     },
35498
35499     // private
35500     setActiveItem : function(item, autoExpand){
35501         if(item != this.activeItem){
35502             if(this.activeItem){
35503                 this.activeItem.deactivate();
35504             }
35505             this.activeItem = item;
35506             item.activate(autoExpand);
35507         }else if(autoExpand){
35508             item.expandMenu();
35509         }
35510     },
35511
35512     // private
35513     tryActivate : function(start, step){
35514         var items = this.items;
35515         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35516             var item = items.get(i);
35517             if(!item.disabled && item.canActivate){
35518                 this.setActiveItem(item, false);
35519                 return item;
35520             }
35521         }
35522         return false;
35523     },
35524
35525     // private
35526     onMouseOver : function(e){
35527         var t;
35528         if(t = this.findTargetItem(e)){
35529             if(t.canActivate && !t.disabled){
35530                 this.setActiveItem(t, true);
35531             }
35532         }
35533         this.fireEvent("mouseover", this, e, t);
35534     },
35535
35536     // private
35537     onMouseOut : function(e){
35538         var t;
35539         if(t = this.findTargetItem(e)){
35540             if(t == this.activeItem && t.shouldDeactivate(e)){
35541                 this.activeItem.deactivate();
35542                 delete this.activeItem;
35543             }
35544         }
35545         this.fireEvent("mouseout", this, e, t);
35546     },
35547
35548     /**
35549      * Read-only.  Returns true if the menu is currently displayed, else false.
35550      * @type Boolean
35551      */
35552     isVisible : function(){
35553         return this.el && !this.hidden;
35554     },
35555
35556     /**
35557      * Displays this menu relative to another element
35558      * @param {String/HTMLElement/Roo.Element} element The element to align to
35559      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35560      * the element (defaults to this.defaultAlign)
35561      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35562      */
35563     show : function(el, pos, parentMenu){
35564         this.parentMenu = parentMenu;
35565         if(!this.el){
35566             this.render();
35567         }
35568         this.fireEvent("beforeshow", this);
35569         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35570     },
35571
35572     /**
35573      * Displays this menu at a specific xy position
35574      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35575      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35576      */
35577     showAt : function(xy, parentMenu, /* private: */_e){
35578         this.parentMenu = parentMenu;
35579         if(!this.el){
35580             this.render();
35581         }
35582         if(_e !== false){
35583             this.fireEvent("beforeshow", this);
35584             xy = this.el.adjustForConstraints(xy);
35585         }
35586         this.el.setXY(xy);
35587         this.el.show();
35588         this.hidden = false;
35589         this.focus();
35590         this.fireEvent("show", this);
35591     },
35592
35593     focus : function(){
35594         if(!this.hidden){
35595             this.doFocus.defer(50, this);
35596         }
35597     },
35598
35599     doFocus : function(){
35600         if(!this.hidden){
35601             this.focusEl.focus();
35602         }
35603     },
35604
35605     /**
35606      * Hides this menu and optionally all parent menus
35607      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35608      */
35609     hide : function(deep){
35610         if(this.el && this.isVisible()){
35611             this.fireEvent("beforehide", this);
35612             if(this.activeItem){
35613                 this.activeItem.deactivate();
35614                 this.activeItem = null;
35615             }
35616             this.el.hide();
35617             this.hidden = true;
35618             this.fireEvent("hide", this);
35619         }
35620         if(deep === true && this.parentMenu){
35621             this.parentMenu.hide(true);
35622         }
35623     },
35624
35625     /**
35626      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35627      * Any of the following are valid:
35628      * <ul>
35629      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35630      * <li>An HTMLElement object which will be converted to a menu item</li>
35631      * <li>A menu item config object that will be created as a new menu item</li>
35632      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35633      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35634      * </ul>
35635      * Usage:
35636      * <pre><code>
35637 // Create the menu
35638 var menu = new Roo.menu.Menu();
35639
35640 // Create a menu item to add by reference
35641 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35642
35643 // Add a bunch of items at once using different methods.
35644 // Only the last item added will be returned.
35645 var item = menu.add(
35646     menuItem,                // add existing item by ref
35647     'Dynamic Item',          // new TextItem
35648     '-',                     // new separator
35649     { text: 'Config Item' }  // new item by config
35650 );
35651 </code></pre>
35652      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35653      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35654      */
35655     add : function(){
35656         var a = arguments, l = a.length, item;
35657         for(var i = 0; i < l; i++){
35658             var el = a[i];
35659             if ((typeof(el) == "object") && el.xtype && el.xns) {
35660                 el = Roo.factory(el, Roo.menu);
35661             }
35662             
35663             if(el.render){ // some kind of Item
35664                 item = this.addItem(el);
35665             }else if(typeof el == "string"){ // string
35666                 if(el == "separator" || el == "-"){
35667                     item = this.addSeparator();
35668                 }else{
35669                     item = this.addText(el);
35670                 }
35671             }else if(el.tagName || el.el){ // element
35672                 item = this.addElement(el);
35673             }else if(typeof el == "object"){ // must be menu item config?
35674                 item = this.addMenuItem(el);
35675             }
35676         }
35677         return item;
35678     },
35679
35680     /**
35681      * Returns this menu's underlying {@link Roo.Element} object
35682      * @return {Roo.Element} The element
35683      */
35684     getEl : function(){
35685         if(!this.el){
35686             this.render();
35687         }
35688         return this.el;
35689     },
35690
35691     /**
35692      * Adds a separator bar to the menu
35693      * @return {Roo.menu.Item} The menu item that was added
35694      */
35695     addSeparator : function(){
35696         return this.addItem(new Roo.menu.Separator());
35697     },
35698
35699     /**
35700      * Adds an {@link Roo.Element} object to the menu
35701      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35702      * @return {Roo.menu.Item} The menu item that was added
35703      */
35704     addElement : function(el){
35705         return this.addItem(new Roo.menu.BaseItem(el));
35706     },
35707
35708     /**
35709      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35710      * @param {Roo.menu.Item} item The menu item to add
35711      * @return {Roo.menu.Item} The menu item that was added
35712      */
35713     addItem : function(item){
35714         this.items.add(item);
35715         if(this.ul){
35716             var li = document.createElement("li");
35717             li.className = "x-menu-list-item";
35718             this.ul.dom.appendChild(li);
35719             item.render(li, this);
35720             this.delayAutoWidth();
35721         }
35722         return item;
35723     },
35724
35725     /**
35726      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35727      * @param {Object} config A MenuItem config object
35728      * @return {Roo.menu.Item} The menu item that was added
35729      */
35730     addMenuItem : function(config){
35731         if(!(config instanceof Roo.menu.Item)){
35732             if(typeof config.checked == "boolean"){ // must be check menu item config?
35733                 config = new Roo.menu.CheckItem(config);
35734             }else{
35735                 config = new Roo.menu.Item(config);
35736             }
35737         }
35738         return this.addItem(config);
35739     },
35740
35741     /**
35742      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35743      * @param {String} text The text to display in the menu item
35744      * @return {Roo.menu.Item} The menu item that was added
35745      */
35746     addText : function(text){
35747         return this.addItem(new Roo.menu.TextItem({ text : text }));
35748     },
35749
35750     /**
35751      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35752      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35753      * @param {Roo.menu.Item} item The menu item to add
35754      * @return {Roo.menu.Item} The menu item that was added
35755      */
35756     insert : function(index, item){
35757         this.items.insert(index, item);
35758         if(this.ul){
35759             var li = document.createElement("li");
35760             li.className = "x-menu-list-item";
35761             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35762             item.render(li, this);
35763             this.delayAutoWidth();
35764         }
35765         return item;
35766     },
35767
35768     /**
35769      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35770      * @param {Roo.menu.Item} item The menu item to remove
35771      */
35772     remove : function(item){
35773         this.items.removeKey(item.id);
35774         item.destroy();
35775     },
35776
35777     /**
35778      * Removes and destroys all items in the menu
35779      */
35780     removeAll : function(){
35781         var f;
35782         while(f = this.items.first()){
35783             this.remove(f);
35784         }
35785     }
35786 });
35787
35788 // MenuNav is a private utility class used internally by the Menu
35789 Roo.menu.MenuNav = function(menu){
35790     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35791     this.scope = this.menu = menu;
35792 };
35793
35794 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35795     doRelay : function(e, h){
35796         var k = e.getKey();
35797         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35798             this.menu.tryActivate(0, 1);
35799             return false;
35800         }
35801         return h.call(this.scope || this, e, this.menu);
35802     },
35803
35804     up : function(e, m){
35805         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35806             m.tryActivate(m.items.length-1, -1);
35807         }
35808     },
35809
35810     down : function(e, m){
35811         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35812             m.tryActivate(0, 1);
35813         }
35814     },
35815
35816     right : function(e, m){
35817         if(m.activeItem){
35818             m.activeItem.expandMenu(true);
35819         }
35820     },
35821
35822     left : function(e, m){
35823         m.hide();
35824         if(m.parentMenu && m.parentMenu.activeItem){
35825             m.parentMenu.activeItem.activate();
35826         }
35827     },
35828
35829     enter : function(e, m){
35830         if(m.activeItem){
35831             e.stopPropagation();
35832             m.activeItem.onClick(e);
35833             m.fireEvent("click", this, m.activeItem);
35834             return true;
35835         }
35836     }
35837 });/*
35838  * Based on:
35839  * Ext JS Library 1.1.1
35840  * Copyright(c) 2006-2007, Ext JS, LLC.
35841  *
35842  * Originally Released Under LGPL - original licence link has changed is not relivant.
35843  *
35844  * Fork - LGPL
35845  * <script type="text/javascript">
35846  */
35847  
35848 /**
35849  * @class Roo.menu.MenuMgr
35850  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35851  * @singleton
35852  */
35853 Roo.menu.MenuMgr = function(){
35854    var menus, active, groups = {}, attached = false, lastShow = new Date();
35855
35856    // private - called when first menu is created
35857    function init(){
35858        menus = {};
35859        active = new Roo.util.MixedCollection();
35860        Roo.get(document).addKeyListener(27, function(){
35861            if(active.length > 0){
35862                hideAll();
35863            }
35864        });
35865    }
35866
35867    // private
35868    function hideAll(){
35869        if(active && active.length > 0){
35870            var c = active.clone();
35871            c.each(function(m){
35872                m.hide();
35873            });
35874        }
35875    }
35876
35877    // private
35878    function onHide(m){
35879        active.remove(m);
35880        if(active.length < 1){
35881            Roo.get(document).un("mousedown", onMouseDown);
35882            attached = false;
35883        }
35884    }
35885
35886    // private
35887    function onShow(m){
35888        var last = active.last();
35889        lastShow = new Date();
35890        active.add(m);
35891        if(!attached){
35892            Roo.get(document).on("mousedown", onMouseDown);
35893            attached = true;
35894        }
35895        if(m.parentMenu){
35896           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35897           m.parentMenu.activeChild = m;
35898        }else if(last && last.isVisible()){
35899           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35900        }
35901    }
35902
35903    // private
35904    function onBeforeHide(m){
35905        if(m.activeChild){
35906            m.activeChild.hide();
35907        }
35908        if(m.autoHideTimer){
35909            clearTimeout(m.autoHideTimer);
35910            delete m.autoHideTimer;
35911        }
35912    }
35913
35914    // private
35915    function onBeforeShow(m){
35916        var pm = m.parentMenu;
35917        if(!pm && !m.allowOtherMenus){
35918            hideAll();
35919        }else if(pm && pm.activeChild && active != m){
35920            pm.activeChild.hide();
35921        }
35922    }
35923
35924    // private
35925    function onMouseDown(e){
35926        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35927            hideAll();
35928        }
35929    }
35930
35931    // private
35932    function onBeforeCheck(mi, state){
35933        if(state){
35934            var g = groups[mi.group];
35935            for(var i = 0, l = g.length; i < l; i++){
35936                if(g[i] != mi){
35937                    g[i].setChecked(false);
35938                }
35939            }
35940        }
35941    }
35942
35943    return {
35944
35945        /**
35946         * Hides all menus that are currently visible
35947         */
35948        hideAll : function(){
35949             hideAll();  
35950        },
35951
35952        // private
35953        register : function(menu){
35954            if(!menus){
35955                init();
35956            }
35957            menus[menu.id] = menu;
35958            menu.on("beforehide", onBeforeHide);
35959            menu.on("hide", onHide);
35960            menu.on("beforeshow", onBeforeShow);
35961            menu.on("show", onShow);
35962            var g = menu.group;
35963            if(g && menu.events["checkchange"]){
35964                if(!groups[g]){
35965                    groups[g] = [];
35966                }
35967                groups[g].push(menu);
35968                menu.on("checkchange", onCheck);
35969            }
35970        },
35971
35972         /**
35973          * Returns a {@link Roo.menu.Menu} object
35974          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35975          * be used to generate and return a new Menu instance.
35976          */
35977        get : function(menu){
35978            if(typeof menu == "string"){ // menu id
35979                return menus[menu];
35980            }else if(menu.events){  // menu instance
35981                return menu;
35982            }else if(typeof menu.length == 'number'){ // array of menu items?
35983                return new Roo.menu.Menu({items:menu});
35984            }else{ // otherwise, must be a config
35985                return new Roo.menu.Menu(menu);
35986            }
35987        },
35988
35989        // private
35990        unregister : function(menu){
35991            delete menus[menu.id];
35992            menu.un("beforehide", onBeforeHide);
35993            menu.un("hide", onHide);
35994            menu.un("beforeshow", onBeforeShow);
35995            menu.un("show", onShow);
35996            var g = menu.group;
35997            if(g && menu.events["checkchange"]){
35998                groups[g].remove(menu);
35999                menu.un("checkchange", onCheck);
36000            }
36001        },
36002
36003        // private
36004        registerCheckable : function(menuItem){
36005            var g = menuItem.group;
36006            if(g){
36007                if(!groups[g]){
36008                    groups[g] = [];
36009                }
36010                groups[g].push(menuItem);
36011                menuItem.on("beforecheckchange", onBeforeCheck);
36012            }
36013        },
36014
36015        // private
36016        unregisterCheckable : function(menuItem){
36017            var g = menuItem.group;
36018            if(g){
36019                groups[g].remove(menuItem);
36020                menuItem.un("beforecheckchange", onBeforeCheck);
36021            }
36022        }
36023    };
36024 }();/*
36025  * Based on:
36026  * Ext JS Library 1.1.1
36027  * Copyright(c) 2006-2007, Ext JS, LLC.
36028  *
36029  * Originally Released Under LGPL - original licence link has changed is not relivant.
36030  *
36031  * Fork - LGPL
36032  * <script type="text/javascript">
36033  */
36034  
36035
36036 /**
36037  * @class Roo.menu.BaseItem
36038  * @extends Roo.Component
36039  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36040  * management and base configuration options shared by all menu components.
36041  * @constructor
36042  * Creates a new BaseItem
36043  * @param {Object} config Configuration options
36044  */
36045 Roo.menu.BaseItem = function(config){
36046     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36047
36048     this.addEvents({
36049         /**
36050          * @event click
36051          * Fires when this item is clicked
36052          * @param {Roo.menu.BaseItem} this
36053          * @param {Roo.EventObject} e
36054          */
36055         click: true,
36056         /**
36057          * @event activate
36058          * Fires when this item is activated
36059          * @param {Roo.menu.BaseItem} this
36060          */
36061         activate : true,
36062         /**
36063          * @event deactivate
36064          * Fires when this item is deactivated
36065          * @param {Roo.menu.BaseItem} this
36066          */
36067         deactivate : true
36068     });
36069
36070     if(this.handler){
36071         this.on("click", this.handler, this.scope, true);
36072     }
36073 };
36074
36075 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36076     /**
36077      * @cfg {Function} handler
36078      * A function that will handle the click event of this menu item (defaults to undefined)
36079      */
36080     /**
36081      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36082      */
36083     canActivate : false,
36084     
36085      /**
36086      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36087      */
36088     hidden: false,
36089     
36090     /**
36091      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36092      */
36093     activeClass : "x-menu-item-active",
36094     /**
36095      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36096      */
36097     hideOnClick : true,
36098     /**
36099      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36100      */
36101     hideDelay : 100,
36102
36103     // private
36104     ctype: "Roo.menu.BaseItem",
36105
36106     // private
36107     actionMode : "container",
36108
36109     // private
36110     render : function(container, parentMenu){
36111         this.parentMenu = parentMenu;
36112         Roo.menu.BaseItem.superclass.render.call(this, container);
36113         this.container.menuItemId = this.id;
36114     },
36115
36116     // private
36117     onRender : function(container, position){
36118         this.el = Roo.get(this.el);
36119         container.dom.appendChild(this.el.dom);
36120     },
36121
36122     // private
36123     onClick : function(e){
36124         if(!this.disabled && this.fireEvent("click", this, e) !== false
36125                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36126             this.handleClick(e);
36127         }else{
36128             e.stopEvent();
36129         }
36130     },
36131
36132     // private
36133     activate : function(){
36134         if(this.disabled){
36135             return false;
36136         }
36137         var li = this.container;
36138         li.addClass(this.activeClass);
36139         this.region = li.getRegion().adjust(2, 2, -2, -2);
36140         this.fireEvent("activate", this);
36141         return true;
36142     },
36143
36144     // private
36145     deactivate : function(){
36146         this.container.removeClass(this.activeClass);
36147         this.fireEvent("deactivate", this);
36148     },
36149
36150     // private
36151     shouldDeactivate : function(e){
36152         return !this.region || !this.region.contains(e.getPoint());
36153     },
36154
36155     // private
36156     handleClick : function(e){
36157         if(this.hideOnClick){
36158             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36159         }
36160     },
36161
36162     // private
36163     expandMenu : function(autoActivate){
36164         // do nothing
36165     },
36166
36167     // private
36168     hideMenu : function(){
36169         // do nothing
36170     }
36171 });/*
36172  * Based on:
36173  * Ext JS Library 1.1.1
36174  * Copyright(c) 2006-2007, Ext JS, LLC.
36175  *
36176  * Originally Released Under LGPL - original licence link has changed is not relivant.
36177  *
36178  * Fork - LGPL
36179  * <script type="text/javascript">
36180  */
36181  
36182 /**
36183  * @class Roo.menu.Adapter
36184  * @extends Roo.menu.BaseItem
36185  * 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.
36186  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36187  * @constructor
36188  * Creates a new Adapter
36189  * @param {Object} config Configuration options
36190  */
36191 Roo.menu.Adapter = function(component, config){
36192     Roo.menu.Adapter.superclass.constructor.call(this, config);
36193     this.component = component;
36194 };
36195 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36196     // private
36197     canActivate : true,
36198
36199     // private
36200     onRender : function(container, position){
36201         this.component.render(container);
36202         this.el = this.component.getEl();
36203     },
36204
36205     // private
36206     activate : function(){
36207         if(this.disabled){
36208             return false;
36209         }
36210         this.component.focus();
36211         this.fireEvent("activate", this);
36212         return true;
36213     },
36214
36215     // private
36216     deactivate : function(){
36217         this.fireEvent("deactivate", this);
36218     },
36219
36220     // private
36221     disable : function(){
36222         this.component.disable();
36223         Roo.menu.Adapter.superclass.disable.call(this);
36224     },
36225
36226     // private
36227     enable : function(){
36228         this.component.enable();
36229         Roo.menu.Adapter.superclass.enable.call(this);
36230     }
36231 });/*
36232  * Based on:
36233  * Ext JS Library 1.1.1
36234  * Copyright(c) 2006-2007, Ext JS, LLC.
36235  *
36236  * Originally Released Under LGPL - original licence link has changed is not relivant.
36237  *
36238  * Fork - LGPL
36239  * <script type="text/javascript">
36240  */
36241
36242 /**
36243  * @class Roo.menu.TextItem
36244  * @extends Roo.menu.BaseItem
36245  * Adds a static text string to a menu, usually used as either a heading or group separator.
36246  * Note: old style constructor with text is still supported.
36247  * 
36248  * @constructor
36249  * Creates a new TextItem
36250  * @param {Object} cfg Configuration
36251  */
36252 Roo.menu.TextItem = function(cfg){
36253     if (typeof(cfg) == 'string') {
36254         this.text = cfg;
36255     } else {
36256         Roo.apply(this,cfg);
36257     }
36258     
36259     Roo.menu.TextItem.superclass.constructor.call(this);
36260 };
36261
36262 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36263     /**
36264      * @cfg {Boolean} text Text to show on item.
36265      */
36266     text : '',
36267     
36268     /**
36269      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36270      */
36271     hideOnClick : false,
36272     /**
36273      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36274      */
36275     itemCls : "x-menu-text",
36276
36277     // private
36278     onRender : function(){
36279         var s = document.createElement("span");
36280         s.className = this.itemCls;
36281         s.innerHTML = this.text;
36282         this.el = s;
36283         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36284     }
36285 });/*
36286  * Based on:
36287  * Ext JS Library 1.1.1
36288  * Copyright(c) 2006-2007, Ext JS, LLC.
36289  *
36290  * Originally Released Under LGPL - original licence link has changed is not relivant.
36291  *
36292  * Fork - LGPL
36293  * <script type="text/javascript">
36294  */
36295
36296 /**
36297  * @class Roo.menu.Separator
36298  * @extends Roo.menu.BaseItem
36299  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36300  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36301  * @constructor
36302  * @param {Object} config Configuration options
36303  */
36304 Roo.menu.Separator = function(config){
36305     Roo.menu.Separator.superclass.constructor.call(this, config);
36306 };
36307
36308 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36309     /**
36310      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36311      */
36312     itemCls : "x-menu-sep",
36313     /**
36314      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36315      */
36316     hideOnClick : false,
36317
36318     // private
36319     onRender : function(li){
36320         var s = document.createElement("span");
36321         s.className = this.itemCls;
36322         s.innerHTML = "&#160;";
36323         this.el = s;
36324         li.addClass("x-menu-sep-li");
36325         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36326     }
36327 });/*
36328  * Based on:
36329  * Ext JS Library 1.1.1
36330  * Copyright(c) 2006-2007, Ext JS, LLC.
36331  *
36332  * Originally Released Under LGPL - original licence link has changed is not relivant.
36333  *
36334  * Fork - LGPL
36335  * <script type="text/javascript">
36336  */
36337 /**
36338  * @class Roo.menu.Item
36339  * @extends Roo.menu.BaseItem
36340  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36341  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36342  * activation and click handling.
36343  * @constructor
36344  * Creates a new Item
36345  * @param {Object} config Configuration options
36346  */
36347 Roo.menu.Item = function(config){
36348     Roo.menu.Item.superclass.constructor.call(this, config);
36349     if(this.menu){
36350         this.menu = Roo.menu.MenuMgr.get(this.menu);
36351     }
36352 };
36353 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36354     
36355     /**
36356      * @cfg {String} text
36357      * The text to show on the menu item.
36358      */
36359     text: '',
36360      /**
36361      * @cfg {String} HTML to render in menu
36362      * The text to show on the menu item (HTML version).
36363      */
36364     html: '',
36365     /**
36366      * @cfg {String} icon
36367      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36368      */
36369     icon: undefined,
36370     /**
36371      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36372      */
36373     itemCls : "x-menu-item",
36374     /**
36375      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36376      */
36377     canActivate : true,
36378     /**
36379      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36380      */
36381     showDelay: 200,
36382     // doc'd in BaseItem
36383     hideDelay: 200,
36384
36385     // private
36386     ctype: "Roo.menu.Item",
36387     
36388     // private
36389     onRender : function(container, position){
36390         var el = document.createElement("a");
36391         el.hideFocus = true;
36392         el.unselectable = "on";
36393         el.href = this.href || "#";
36394         if(this.hrefTarget){
36395             el.target = this.hrefTarget;
36396         }
36397         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36398         
36399         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36400         
36401         el.innerHTML = String.format(
36402                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36403                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36404         this.el = el;
36405         Roo.menu.Item.superclass.onRender.call(this, container, position);
36406     },
36407
36408     /**
36409      * Sets the text to display in this menu item
36410      * @param {String} text The text to display
36411      * @param {Boolean} isHTML true to indicate text is pure html.
36412      */
36413     setText : function(text, isHTML){
36414         if (isHTML) {
36415             this.html = text;
36416         } else {
36417             this.text = text;
36418             this.html = '';
36419         }
36420         if(this.rendered){
36421             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36422      
36423             this.el.update(String.format(
36424                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36425                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36426             this.parentMenu.autoWidth();
36427         }
36428     },
36429
36430     // private
36431     handleClick : function(e){
36432         if(!this.href){ // if no link defined, stop the event automatically
36433             e.stopEvent();
36434         }
36435         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36436     },
36437
36438     // private
36439     activate : function(autoExpand){
36440         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36441             this.focus();
36442             if(autoExpand){
36443                 this.expandMenu();
36444             }
36445         }
36446         return true;
36447     },
36448
36449     // private
36450     shouldDeactivate : function(e){
36451         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36452             if(this.menu && this.menu.isVisible()){
36453                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36454             }
36455             return true;
36456         }
36457         return false;
36458     },
36459
36460     // private
36461     deactivate : function(){
36462         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36463         this.hideMenu();
36464     },
36465
36466     // private
36467     expandMenu : function(autoActivate){
36468         if(!this.disabled && this.menu){
36469             clearTimeout(this.hideTimer);
36470             delete this.hideTimer;
36471             if(!this.menu.isVisible() && !this.showTimer){
36472                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36473             }else if (this.menu.isVisible() && autoActivate){
36474                 this.menu.tryActivate(0, 1);
36475             }
36476         }
36477     },
36478
36479     // private
36480     deferExpand : function(autoActivate){
36481         delete this.showTimer;
36482         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36483         if(autoActivate){
36484             this.menu.tryActivate(0, 1);
36485         }
36486     },
36487
36488     // private
36489     hideMenu : function(){
36490         clearTimeout(this.showTimer);
36491         delete this.showTimer;
36492         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36493             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36494         }
36495     },
36496
36497     // private
36498     deferHide : function(){
36499         delete this.hideTimer;
36500         this.menu.hide();
36501     }
36502 });/*
36503  * Based on:
36504  * Ext JS Library 1.1.1
36505  * Copyright(c) 2006-2007, Ext JS, LLC.
36506  *
36507  * Originally Released Under LGPL - original licence link has changed is not relivant.
36508  *
36509  * Fork - LGPL
36510  * <script type="text/javascript">
36511  */
36512  
36513 /**
36514  * @class Roo.menu.CheckItem
36515  * @extends Roo.menu.Item
36516  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36517  * @constructor
36518  * Creates a new CheckItem
36519  * @param {Object} config Configuration options
36520  */
36521 Roo.menu.CheckItem = function(config){
36522     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36523     this.addEvents({
36524         /**
36525          * @event beforecheckchange
36526          * Fires before the checked value is set, providing an opportunity to cancel if needed
36527          * @param {Roo.menu.CheckItem} this
36528          * @param {Boolean} checked The new checked value that will be set
36529          */
36530         "beforecheckchange" : true,
36531         /**
36532          * @event checkchange
36533          * Fires after the checked value has been set
36534          * @param {Roo.menu.CheckItem} this
36535          * @param {Boolean} checked The checked value that was set
36536          */
36537         "checkchange" : true
36538     });
36539     if(this.checkHandler){
36540         this.on('checkchange', this.checkHandler, this.scope);
36541     }
36542 };
36543 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36544     /**
36545      * @cfg {String} group
36546      * All check items with the same group name will automatically be grouped into a single-select
36547      * radio button group (defaults to '')
36548      */
36549     /**
36550      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36551      */
36552     itemCls : "x-menu-item x-menu-check-item",
36553     /**
36554      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36555      */
36556     groupClass : "x-menu-group-item",
36557
36558     /**
36559      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36560      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36561      * initialized with checked = true will be rendered as checked.
36562      */
36563     checked: false,
36564
36565     // private
36566     ctype: "Roo.menu.CheckItem",
36567
36568     // private
36569     onRender : function(c){
36570         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36571         if(this.group){
36572             this.el.addClass(this.groupClass);
36573         }
36574         Roo.menu.MenuMgr.registerCheckable(this);
36575         if(this.checked){
36576             this.checked = false;
36577             this.setChecked(true, true);
36578         }
36579     },
36580
36581     // private
36582     destroy : function(){
36583         if(this.rendered){
36584             Roo.menu.MenuMgr.unregisterCheckable(this);
36585         }
36586         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36587     },
36588
36589     /**
36590      * Set the checked state of this item
36591      * @param {Boolean} checked The new checked value
36592      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36593      */
36594     setChecked : function(state, suppressEvent){
36595         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36596             if(this.container){
36597                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36598             }
36599             this.checked = state;
36600             if(suppressEvent !== true){
36601                 this.fireEvent("checkchange", this, state);
36602             }
36603         }
36604     },
36605
36606     // private
36607     handleClick : function(e){
36608        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36609            this.setChecked(!this.checked);
36610        }
36611        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36612     }
36613 });/*
36614  * Based on:
36615  * Ext JS Library 1.1.1
36616  * Copyright(c) 2006-2007, Ext JS, LLC.
36617  *
36618  * Originally Released Under LGPL - original licence link has changed is not relivant.
36619  *
36620  * Fork - LGPL
36621  * <script type="text/javascript">
36622  */
36623  
36624 /**
36625  * @class Roo.menu.DateItem
36626  * @extends Roo.menu.Adapter
36627  * A menu item that wraps the {@link Roo.DatPicker} component.
36628  * @constructor
36629  * Creates a new DateItem
36630  * @param {Object} config Configuration options
36631  */
36632 Roo.menu.DateItem = function(config){
36633     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36634     /** The Roo.DatePicker object @type Roo.DatePicker */
36635     this.picker = this.component;
36636     this.addEvents({select: true});
36637     
36638     this.picker.on("render", function(picker){
36639         picker.getEl().swallowEvent("click");
36640         picker.container.addClass("x-menu-date-item");
36641     });
36642
36643     this.picker.on("select", this.onSelect, this);
36644 };
36645
36646 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36647     // private
36648     onSelect : function(picker, date){
36649         this.fireEvent("select", this, date, picker);
36650         Roo.menu.DateItem.superclass.handleClick.call(this);
36651     }
36652 });/*
36653  * Based on:
36654  * Ext JS Library 1.1.1
36655  * Copyright(c) 2006-2007, Ext JS, LLC.
36656  *
36657  * Originally Released Under LGPL - original licence link has changed is not relivant.
36658  *
36659  * Fork - LGPL
36660  * <script type="text/javascript">
36661  */
36662  
36663 /**
36664  * @class Roo.menu.ColorItem
36665  * @extends Roo.menu.Adapter
36666  * A menu item that wraps the {@link Roo.ColorPalette} component.
36667  * @constructor
36668  * Creates a new ColorItem
36669  * @param {Object} config Configuration options
36670  */
36671 Roo.menu.ColorItem = function(config){
36672     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36673     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36674     this.palette = this.component;
36675     this.relayEvents(this.palette, ["select"]);
36676     if(this.selectHandler){
36677         this.on('select', this.selectHandler, this.scope);
36678     }
36679 };
36680 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36681  * Based on:
36682  * Ext JS Library 1.1.1
36683  * Copyright(c) 2006-2007, Ext JS, LLC.
36684  *
36685  * Originally Released Under LGPL - original licence link has changed is not relivant.
36686  *
36687  * Fork - LGPL
36688  * <script type="text/javascript">
36689  */
36690  
36691
36692 /**
36693  * @class Roo.menu.DateMenu
36694  * @extends Roo.menu.Menu
36695  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36696  * @constructor
36697  * Creates a new DateMenu
36698  * @param {Object} config Configuration options
36699  */
36700 Roo.menu.DateMenu = function(config){
36701     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36702     this.plain = true;
36703     var di = new Roo.menu.DateItem(config);
36704     this.add(di);
36705     /**
36706      * The {@link Roo.DatePicker} instance for this DateMenu
36707      * @type DatePicker
36708      */
36709     this.picker = di.picker;
36710     /**
36711      * @event select
36712      * @param {DatePicker} picker
36713      * @param {Date} date
36714      */
36715     this.relayEvents(di, ["select"]);
36716     this.on('beforeshow', function(){
36717         if(this.picker){
36718             this.picker.hideMonthPicker(false);
36719         }
36720     }, this);
36721 };
36722 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36723     cls:'x-date-menu'
36724 });/*
36725  * Based on:
36726  * Ext JS Library 1.1.1
36727  * Copyright(c) 2006-2007, Ext JS, LLC.
36728  *
36729  * Originally Released Under LGPL - original licence link has changed is not relivant.
36730  *
36731  * Fork - LGPL
36732  * <script type="text/javascript">
36733  */
36734  
36735
36736 /**
36737  * @class Roo.menu.ColorMenu
36738  * @extends Roo.menu.Menu
36739  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36740  * @constructor
36741  * Creates a new ColorMenu
36742  * @param {Object} config Configuration options
36743  */
36744 Roo.menu.ColorMenu = function(config){
36745     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36746     this.plain = true;
36747     var ci = new Roo.menu.ColorItem(config);
36748     this.add(ci);
36749     /**
36750      * The {@link Roo.ColorPalette} instance for this ColorMenu
36751      * @type ColorPalette
36752      */
36753     this.palette = ci.palette;
36754     /**
36755      * @event select
36756      * @param {ColorPalette} palette
36757      * @param {String} color
36758      */
36759     this.relayEvents(ci, ["select"]);
36760 };
36761 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36762  * Based on:
36763  * Ext JS Library 1.1.1
36764  * Copyright(c) 2006-2007, Ext JS, LLC.
36765  *
36766  * Originally Released Under LGPL - original licence link has changed is not relivant.
36767  *
36768  * Fork - LGPL
36769  * <script type="text/javascript">
36770  */
36771  
36772 /**
36773  * @class Roo.form.Field
36774  * @extends Roo.BoxComponent
36775  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36776  * @constructor
36777  * Creates a new Field
36778  * @param {Object} config Configuration options
36779  */
36780 Roo.form.Field = function(config){
36781     Roo.form.Field.superclass.constructor.call(this, config);
36782 };
36783
36784 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36785     /**
36786      * @cfg {String} fieldLabel Label to use when rendering a form.
36787      */
36788        /**
36789      * @cfg {String} qtip Mouse over tip
36790      */
36791      
36792     /**
36793      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36794      */
36795     invalidClass : "x-form-invalid",
36796     /**
36797      * @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")
36798      */
36799     invalidText : "The value in this field is invalid",
36800     /**
36801      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36802      */
36803     focusClass : "x-form-focus",
36804     /**
36805      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36806       automatic validation (defaults to "keyup").
36807      */
36808     validationEvent : "keyup",
36809     /**
36810      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36811      */
36812     validateOnBlur : true,
36813     /**
36814      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36815      */
36816     validationDelay : 250,
36817     /**
36818      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36819      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36820      */
36821     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36822     /**
36823      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36824      */
36825     fieldClass : "x-form-field",
36826     /**
36827      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36828      *<pre>
36829 Value         Description
36830 -----------   ----------------------------------------------------------------------
36831 qtip          Display a quick tip when the user hovers over the field
36832 title         Display a default browser title attribute popup
36833 under         Add a block div beneath the field containing the error text
36834 side          Add an error icon to the right of the field with a popup on hover
36835 [element id]  Add the error text directly to the innerHTML of the specified element
36836 </pre>
36837      */
36838     msgTarget : 'qtip',
36839     /**
36840      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36841      */
36842     msgFx : 'normal',
36843
36844     /**
36845      * @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.
36846      */
36847     readOnly : false,
36848
36849     /**
36850      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36851      */
36852     disabled : false,
36853
36854     /**
36855      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36856      */
36857     inputType : undefined,
36858     
36859     /**
36860      * @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).
36861          */
36862         tabIndex : undefined,
36863         
36864     // private
36865     isFormField : true,
36866
36867     // private
36868     hasFocus : false,
36869     /**
36870      * @property {Roo.Element} fieldEl
36871      * Element Containing the rendered Field (with label etc.)
36872      */
36873     /**
36874      * @cfg {Mixed} value A value to initialize this field with.
36875      */
36876     value : undefined,
36877
36878     /**
36879      * @cfg {String} name The field's HTML name attribute.
36880      */
36881     /**
36882      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36883      */
36884
36885         // private ??
36886         initComponent : function(){
36887         Roo.form.Field.superclass.initComponent.call(this);
36888         this.addEvents({
36889             /**
36890              * @event focus
36891              * Fires when this field receives input focus.
36892              * @param {Roo.form.Field} this
36893              */
36894             focus : true,
36895             /**
36896              * @event blur
36897              * Fires when this field loses input focus.
36898              * @param {Roo.form.Field} this
36899              */
36900             blur : true,
36901             /**
36902              * @event specialkey
36903              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36904              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36905              * @param {Roo.form.Field} this
36906              * @param {Roo.EventObject} e The event object
36907              */
36908             specialkey : true,
36909             /**
36910              * @event change
36911              * Fires just before the field blurs if the field value has changed.
36912              * @param {Roo.form.Field} this
36913              * @param {Mixed} newValue The new value
36914              * @param {Mixed} oldValue The original value
36915              */
36916             change : true,
36917             /**
36918              * @event invalid
36919              * Fires after the field has been marked as invalid.
36920              * @param {Roo.form.Field} this
36921              * @param {String} msg The validation message
36922              */
36923             invalid : true,
36924             /**
36925              * @event valid
36926              * Fires after the field has been validated with no errors.
36927              * @param {Roo.form.Field} this
36928              */
36929             valid : true,
36930              /**
36931              * @event keyup
36932              * Fires after the key up
36933              * @param {Roo.form.Field} this
36934              * @param {Roo.EventObject}  e The event Object
36935              */
36936             keyup : true
36937         });
36938     },
36939
36940     /**
36941      * Returns the name attribute of the field if available
36942      * @return {String} name The field name
36943      */
36944     getName: function(){
36945          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36946     },
36947
36948     // private
36949     onRender : function(ct, position){
36950         Roo.form.Field.superclass.onRender.call(this, ct, position);
36951         if(!this.el){
36952             var cfg = this.getAutoCreate();
36953             if(!cfg.name){
36954                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36955             }
36956             if (!cfg.name.length) {
36957                 delete cfg.name;
36958             }
36959             if(this.inputType){
36960                 cfg.type = this.inputType;
36961             }
36962             this.el = ct.createChild(cfg, position);
36963         }
36964         var type = this.el.dom.type;
36965         if(type){
36966             if(type == 'password'){
36967                 type = 'text';
36968             }
36969             this.el.addClass('x-form-'+type);
36970         }
36971         if(this.readOnly){
36972             this.el.dom.readOnly = true;
36973         }
36974         if(this.tabIndex !== undefined){
36975             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36976         }
36977
36978         this.el.addClass([this.fieldClass, this.cls]);
36979         this.initValue();
36980     },
36981
36982     /**
36983      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36984      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36985      * @return {Roo.form.Field} this
36986      */
36987     applyTo : function(target){
36988         this.allowDomMove = false;
36989         this.el = Roo.get(target);
36990         this.render(this.el.dom.parentNode);
36991         return this;
36992     },
36993
36994     // private
36995     initValue : function(){
36996         if(this.value !== undefined){
36997             this.setValue(this.value);
36998         }else if(this.el.dom.value.length > 0){
36999             this.setValue(this.el.dom.value);
37000         }
37001     },
37002
37003     /**
37004      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37005      */
37006     isDirty : function() {
37007         if(this.disabled) {
37008             return false;
37009         }
37010         return String(this.getValue()) !== String(this.originalValue);
37011     },
37012
37013     // private
37014     afterRender : function(){
37015         Roo.form.Field.superclass.afterRender.call(this);
37016         this.initEvents();
37017     },
37018
37019     // private
37020     fireKey : function(e){
37021         //Roo.log('field ' + e.getKey());
37022         if(e.isNavKeyPress()){
37023             this.fireEvent("specialkey", this, e);
37024         }
37025     },
37026
37027     /**
37028      * Resets the current field value to the originally loaded value and clears any validation messages
37029      */
37030     reset : function(){
37031         this.setValue(this.resetValue);
37032         this.clearInvalid();
37033     },
37034
37035     // private
37036     initEvents : function(){
37037         // safari killled keypress - so keydown is now used..
37038         this.el.on("keydown" , this.fireKey,  this);
37039         this.el.on("focus", this.onFocus,  this);
37040         this.el.on("blur", this.onBlur,  this);
37041         this.el.relayEvent('keyup', this);
37042
37043         // reference to original value for reset
37044         this.originalValue = this.getValue();
37045         this.resetValue =  this.getValue();
37046     },
37047
37048     // private
37049     onFocus : function(){
37050         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37051             this.el.addClass(this.focusClass);
37052         }
37053         if(!this.hasFocus){
37054             this.hasFocus = true;
37055             this.startValue = this.getValue();
37056             this.fireEvent("focus", this);
37057         }
37058     },
37059
37060     beforeBlur : Roo.emptyFn,
37061
37062     // private
37063     onBlur : function(){
37064         this.beforeBlur();
37065         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37066             this.el.removeClass(this.focusClass);
37067         }
37068         this.hasFocus = false;
37069         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37070             this.validate();
37071         }
37072         var v = this.getValue();
37073         if(String(v) !== String(this.startValue)){
37074             this.fireEvent('change', this, v, this.startValue);
37075         }
37076         this.fireEvent("blur", this);
37077     },
37078
37079     /**
37080      * Returns whether or not the field value is currently valid
37081      * @param {Boolean} preventMark True to disable marking the field invalid
37082      * @return {Boolean} True if the value is valid, else false
37083      */
37084     isValid : function(preventMark){
37085         if(this.disabled){
37086             return true;
37087         }
37088         var restore = this.preventMark;
37089         this.preventMark = preventMark === true;
37090         var v = this.validateValue(this.processValue(this.getRawValue()));
37091         this.preventMark = restore;
37092         return v;
37093     },
37094
37095     /**
37096      * Validates the field value
37097      * @return {Boolean} True if the value is valid, else false
37098      */
37099     validate : function(){
37100         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37101             this.clearInvalid();
37102             return true;
37103         }
37104         return false;
37105     },
37106
37107     processValue : function(value){
37108         return value;
37109     },
37110
37111     // private
37112     // Subclasses should provide the validation implementation by overriding this
37113     validateValue : function(value){
37114         return true;
37115     },
37116
37117     /**
37118      * Mark this field as invalid
37119      * @param {String} msg The validation message
37120      */
37121     markInvalid : function(msg){
37122         if(!this.rendered || this.preventMark){ // not rendered
37123             return;
37124         }
37125         
37126         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37127         
37128         obj.el.addClass(this.invalidClass);
37129         msg = msg || this.invalidText;
37130         switch(this.msgTarget){
37131             case 'qtip':
37132                 obj.el.dom.qtip = msg;
37133                 obj.el.dom.qclass = 'x-form-invalid-tip';
37134                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37135                     Roo.QuickTips.enable();
37136                 }
37137                 break;
37138             case 'title':
37139                 this.el.dom.title = msg;
37140                 break;
37141             case 'under':
37142                 if(!this.errorEl){
37143                     var elp = this.el.findParent('.x-form-element', 5, true);
37144                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37145                     this.errorEl.setWidth(elp.getWidth(true)-20);
37146                 }
37147                 this.errorEl.update(msg);
37148                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37149                 break;
37150             case 'side':
37151                 if(!this.errorIcon){
37152                     var elp = this.el.findParent('.x-form-element', 5, true);
37153                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37154                 }
37155                 this.alignErrorIcon();
37156                 this.errorIcon.dom.qtip = msg;
37157                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37158                 this.errorIcon.show();
37159                 this.on('resize', this.alignErrorIcon, this);
37160                 break;
37161             default:
37162                 var t = Roo.getDom(this.msgTarget);
37163                 t.innerHTML = msg;
37164                 t.style.display = this.msgDisplay;
37165                 break;
37166         }
37167         this.fireEvent('invalid', this, msg);
37168     },
37169
37170     // private
37171     alignErrorIcon : function(){
37172         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37173     },
37174
37175     /**
37176      * Clear any invalid styles/messages for this field
37177      */
37178     clearInvalid : function(){
37179         if(!this.rendered || this.preventMark){ // not rendered
37180             return;
37181         }
37182         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37183         
37184         obj.el.removeClass(this.invalidClass);
37185         switch(this.msgTarget){
37186             case 'qtip':
37187                 obj.el.dom.qtip = '';
37188                 break;
37189             case 'title':
37190                 this.el.dom.title = '';
37191                 break;
37192             case 'under':
37193                 if(this.errorEl){
37194                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37195                 }
37196                 break;
37197             case 'side':
37198                 if(this.errorIcon){
37199                     this.errorIcon.dom.qtip = '';
37200                     this.errorIcon.hide();
37201                     this.un('resize', this.alignErrorIcon, this);
37202                 }
37203                 break;
37204             default:
37205                 var t = Roo.getDom(this.msgTarget);
37206                 t.innerHTML = '';
37207                 t.style.display = 'none';
37208                 break;
37209         }
37210         this.fireEvent('valid', this);
37211     },
37212
37213     /**
37214      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37215      * @return {Mixed} value The field value
37216      */
37217     getRawValue : function(){
37218         var v = this.el.getValue();
37219         
37220         return v;
37221     },
37222
37223     /**
37224      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37225      * @return {Mixed} value The field value
37226      */
37227     getValue : function(){
37228         var v = this.el.getValue();
37229          
37230         return v;
37231     },
37232
37233     /**
37234      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37235      * @param {Mixed} value The value to set
37236      */
37237     setRawValue : function(v){
37238         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37239     },
37240
37241     /**
37242      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37243      * @param {Mixed} value The value to set
37244      */
37245     setValue : function(v){
37246         this.value = v;
37247         if(this.rendered){
37248             this.el.dom.value = (v === null || v === undefined ? '' : v);
37249              this.validate();
37250         }
37251     },
37252
37253     adjustSize : function(w, h){
37254         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37255         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37256         return s;
37257     },
37258
37259     adjustWidth : function(tag, w){
37260         tag = tag.toLowerCase();
37261         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37262             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37263                 if(tag == 'input'){
37264                     return w + 2;
37265                 }
37266                 if(tag == 'textarea'){
37267                     return w-2;
37268                 }
37269             }else if(Roo.isOpera){
37270                 if(tag == 'input'){
37271                     return w + 2;
37272                 }
37273                 if(tag == 'textarea'){
37274                     return w-2;
37275                 }
37276             }
37277         }
37278         return w;
37279     }
37280 });
37281
37282
37283 // anything other than normal should be considered experimental
37284 Roo.form.Field.msgFx = {
37285     normal : {
37286         show: function(msgEl, f){
37287             msgEl.setDisplayed('block');
37288         },
37289
37290         hide : function(msgEl, f){
37291             msgEl.setDisplayed(false).update('');
37292         }
37293     },
37294
37295     slide : {
37296         show: function(msgEl, f){
37297             msgEl.slideIn('t', {stopFx:true});
37298         },
37299
37300         hide : function(msgEl, f){
37301             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37302         }
37303     },
37304
37305     slideRight : {
37306         show: function(msgEl, f){
37307             msgEl.fixDisplay();
37308             msgEl.alignTo(f.el, 'tl-tr');
37309             msgEl.slideIn('l', {stopFx:true});
37310         },
37311
37312         hide : function(msgEl, f){
37313             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37314         }
37315     }
37316 };/*
37317  * Based on:
37318  * Ext JS Library 1.1.1
37319  * Copyright(c) 2006-2007, Ext JS, LLC.
37320  *
37321  * Originally Released Under LGPL - original licence link has changed is not relivant.
37322  *
37323  * Fork - LGPL
37324  * <script type="text/javascript">
37325  */
37326  
37327
37328 /**
37329  * @class Roo.form.TextField
37330  * @extends Roo.form.Field
37331  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37332  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37333  * @constructor
37334  * Creates a new TextField
37335  * @param {Object} config Configuration options
37336  */
37337 Roo.form.TextField = function(config){
37338     Roo.form.TextField.superclass.constructor.call(this, config);
37339     this.addEvents({
37340         /**
37341          * @event autosize
37342          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37343          * according to the default logic, but this event provides a hook for the developer to apply additional
37344          * logic at runtime to resize the field if needed.
37345              * @param {Roo.form.Field} this This text field
37346              * @param {Number} width The new field width
37347              */
37348         autosize : true
37349     });
37350 };
37351
37352 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37353     /**
37354      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37355      */
37356     grow : false,
37357     /**
37358      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37359      */
37360     growMin : 30,
37361     /**
37362      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37363      */
37364     growMax : 800,
37365     /**
37366      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37367      */
37368     vtype : null,
37369     /**
37370      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37371      */
37372     maskRe : null,
37373     /**
37374      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37375      */
37376     disableKeyFilter : false,
37377     /**
37378      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37379      */
37380     allowBlank : true,
37381     /**
37382      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37383      */
37384     minLength : 0,
37385     /**
37386      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37387      */
37388     maxLength : Number.MAX_VALUE,
37389     /**
37390      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37391      */
37392     minLengthText : "The minimum length for this field is {0}",
37393     /**
37394      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37395      */
37396     maxLengthText : "The maximum length for this field is {0}",
37397     /**
37398      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37399      */
37400     selectOnFocus : false,
37401     /**
37402      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37403      */
37404     blankText : "This field is required",
37405     /**
37406      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37407      * If available, this function will be called only after the basic validators all return true, and will be passed the
37408      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37409      */
37410     validator : null,
37411     /**
37412      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37413      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37414      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37415      */
37416     regex : null,
37417     /**
37418      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37419      */
37420     regexText : "",
37421     /**
37422      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37423      */
37424     emptyText : null,
37425    
37426
37427     // private
37428     initEvents : function()
37429     {
37430         if (this.emptyText) {
37431             this.el.attr('placeholder', this.emptyText);
37432         }
37433         
37434         Roo.form.TextField.superclass.initEvents.call(this);
37435         if(this.validationEvent == 'keyup'){
37436             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37437             this.el.on('keyup', this.filterValidation, this);
37438         }
37439         else if(this.validationEvent !== false){
37440             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37441         }
37442         
37443         if(this.selectOnFocus){
37444             this.on("focus", this.preFocus, this);
37445             
37446         }
37447         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37448             this.el.on("keypress", this.filterKeys, this);
37449         }
37450         if(this.grow){
37451             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37452             this.el.on("click", this.autoSize,  this);
37453         }
37454         if(this.el.is('input[type=password]') && Roo.isSafari){
37455             this.el.on('keydown', this.SafariOnKeyDown, this);
37456         }
37457     },
37458
37459     processValue : function(value){
37460         if(this.stripCharsRe){
37461             var newValue = value.replace(this.stripCharsRe, '');
37462             if(newValue !== value){
37463                 this.setRawValue(newValue);
37464                 return newValue;
37465             }
37466         }
37467         return value;
37468     },
37469
37470     filterValidation : function(e){
37471         if(!e.isNavKeyPress()){
37472             this.validationTask.delay(this.validationDelay);
37473         }
37474     },
37475
37476     // private
37477     onKeyUp : function(e){
37478         if(!e.isNavKeyPress()){
37479             this.autoSize();
37480         }
37481     },
37482
37483     /**
37484      * Resets the current field value to the originally-loaded value and clears any validation messages.
37485      *  
37486      */
37487     reset : function(){
37488         Roo.form.TextField.superclass.reset.call(this);
37489        
37490     },
37491
37492     
37493     // private
37494     preFocus : function(){
37495         
37496         if(this.selectOnFocus){
37497             this.el.dom.select();
37498         }
37499     },
37500
37501     
37502     // private
37503     filterKeys : function(e){
37504         var k = e.getKey();
37505         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37506             return;
37507         }
37508         var c = e.getCharCode(), cc = String.fromCharCode(c);
37509         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37510             return;
37511         }
37512         if(!this.maskRe.test(cc)){
37513             e.stopEvent();
37514         }
37515     },
37516
37517     setValue : function(v){
37518         
37519         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37520         
37521         this.autoSize();
37522     },
37523
37524     /**
37525      * Validates a value according to the field's validation rules and marks the field as invalid
37526      * if the validation fails
37527      * @param {Mixed} value The value to validate
37528      * @return {Boolean} True if the value is valid, else false
37529      */
37530     validateValue : function(value){
37531         if(value.length < 1)  { // if it's blank
37532              if(this.allowBlank){
37533                 this.clearInvalid();
37534                 return true;
37535              }else{
37536                 this.markInvalid(this.blankText);
37537                 return false;
37538              }
37539         }
37540         if(value.length < this.minLength){
37541             this.markInvalid(String.format(this.minLengthText, this.minLength));
37542             return false;
37543         }
37544         if(value.length > this.maxLength){
37545             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37546             return false;
37547         }
37548         if(this.vtype){
37549             var vt = Roo.form.VTypes;
37550             if(!vt[this.vtype](value, this)){
37551                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37552                 return false;
37553             }
37554         }
37555         if(typeof this.validator == "function"){
37556             var msg = this.validator(value);
37557             if(msg !== true){
37558                 this.markInvalid(msg);
37559                 return false;
37560             }
37561         }
37562         if(this.regex && !this.regex.test(value)){
37563             this.markInvalid(this.regexText);
37564             return false;
37565         }
37566         return true;
37567     },
37568
37569     /**
37570      * Selects text in this field
37571      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37572      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37573      */
37574     selectText : function(start, end){
37575         var v = this.getRawValue();
37576         if(v.length > 0){
37577             start = start === undefined ? 0 : start;
37578             end = end === undefined ? v.length : end;
37579             var d = this.el.dom;
37580             if(d.setSelectionRange){
37581                 d.setSelectionRange(start, end);
37582             }else if(d.createTextRange){
37583                 var range = d.createTextRange();
37584                 range.moveStart("character", start);
37585                 range.moveEnd("character", v.length-end);
37586                 range.select();
37587             }
37588         }
37589     },
37590
37591     /**
37592      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37593      * This only takes effect if grow = true, and fires the autosize event.
37594      */
37595     autoSize : function(){
37596         if(!this.grow || !this.rendered){
37597             return;
37598         }
37599         if(!this.metrics){
37600             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37601         }
37602         var el = this.el;
37603         var v = el.dom.value;
37604         var d = document.createElement('div');
37605         d.appendChild(document.createTextNode(v));
37606         v = d.innerHTML;
37607         d = null;
37608         v += "&#160;";
37609         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37610         this.el.setWidth(w);
37611         this.fireEvent("autosize", this, w);
37612     },
37613     
37614     // private
37615     SafariOnKeyDown : function(event)
37616     {
37617         // this is a workaround for a password hang bug on chrome/ webkit.
37618         
37619         var isSelectAll = false;
37620         
37621         if(this.el.dom.selectionEnd > 0){
37622             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37623         }
37624         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37625             event.preventDefault();
37626             this.setValue('');
37627             return;
37628         }
37629         
37630         if(isSelectAll){ // backspace and delete key
37631             
37632             event.preventDefault();
37633             // this is very hacky as keydown always get's upper case.
37634             //
37635             var cc = String.fromCharCode(event.getCharCode());
37636             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37637             
37638         }
37639         
37640         
37641     }
37642 });/*
37643  * Based on:
37644  * Ext JS Library 1.1.1
37645  * Copyright(c) 2006-2007, Ext JS, LLC.
37646  *
37647  * Originally Released Under LGPL - original licence link has changed is not relivant.
37648  *
37649  * Fork - LGPL
37650  * <script type="text/javascript">
37651  */
37652  
37653 /**
37654  * @class Roo.form.Hidden
37655  * @extends Roo.form.TextField
37656  * Simple Hidden element used on forms 
37657  * 
37658  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37659  * 
37660  * @constructor
37661  * Creates a new Hidden form element.
37662  * @param {Object} config Configuration options
37663  */
37664
37665
37666
37667 // easy hidden field...
37668 Roo.form.Hidden = function(config){
37669     Roo.form.Hidden.superclass.constructor.call(this, config);
37670 };
37671   
37672 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37673     fieldLabel:      '',
37674     inputType:      'hidden',
37675     width:          50,
37676     allowBlank:     true,
37677     labelSeparator: '',
37678     hidden:         true,
37679     itemCls :       'x-form-item-display-none'
37680
37681
37682 });
37683
37684
37685 /*
37686  * Based on:
37687  * Ext JS Library 1.1.1
37688  * Copyright(c) 2006-2007, Ext JS, LLC.
37689  *
37690  * Originally Released Under LGPL - original licence link has changed is not relivant.
37691  *
37692  * Fork - LGPL
37693  * <script type="text/javascript">
37694  */
37695  
37696 /**
37697  * @class Roo.form.TriggerField
37698  * @extends Roo.form.TextField
37699  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37700  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37701  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37702  * for which you can provide a custom implementation.  For example:
37703  * <pre><code>
37704 var trigger = new Roo.form.TriggerField();
37705 trigger.onTriggerClick = myTriggerFn;
37706 trigger.applyTo('my-field');
37707 </code></pre>
37708  *
37709  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37710  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37711  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37712  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37713  * @constructor
37714  * Create a new TriggerField.
37715  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37716  * to the base TextField)
37717  */
37718 Roo.form.TriggerField = function(config){
37719     this.mimicing = false;
37720     Roo.form.TriggerField.superclass.constructor.call(this, config);
37721 };
37722
37723 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37724     /**
37725      * @cfg {String} triggerClass A CSS class to apply to the trigger
37726      */
37727     /**
37728      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37729      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37730      */
37731     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37732     /**
37733      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37734      */
37735     hideTrigger:false,
37736
37737     /** @cfg {Boolean} grow @hide */
37738     /** @cfg {Number} growMin @hide */
37739     /** @cfg {Number} growMax @hide */
37740
37741     /**
37742      * @hide 
37743      * @method
37744      */
37745     autoSize: Roo.emptyFn,
37746     // private
37747     monitorTab : true,
37748     // private
37749     deferHeight : true,
37750
37751     
37752     actionMode : 'wrap',
37753     // private
37754     onResize : function(w, h){
37755         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37756         if(typeof w == 'number'){
37757             var x = w - this.trigger.getWidth();
37758             this.el.setWidth(this.adjustWidth('input', x));
37759             this.trigger.setStyle('left', x+'px');
37760         }
37761     },
37762
37763     // private
37764     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37765
37766     // private
37767     getResizeEl : function(){
37768         return this.wrap;
37769     },
37770
37771     // private
37772     getPositionEl : function(){
37773         return this.wrap;
37774     },
37775
37776     // private
37777     alignErrorIcon : function(){
37778         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37779     },
37780
37781     // private
37782     onRender : function(ct, position){
37783         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37784         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37785         this.trigger = this.wrap.createChild(this.triggerConfig ||
37786                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37787         if(this.hideTrigger){
37788             this.trigger.setDisplayed(false);
37789         }
37790         this.initTrigger();
37791         if(!this.width){
37792             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37793         }
37794     },
37795
37796     // private
37797     initTrigger : function(){
37798         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37799         this.trigger.addClassOnOver('x-form-trigger-over');
37800         this.trigger.addClassOnClick('x-form-trigger-click');
37801     },
37802
37803     // private
37804     onDestroy : function(){
37805         if(this.trigger){
37806             this.trigger.removeAllListeners();
37807             this.trigger.remove();
37808         }
37809         if(this.wrap){
37810             this.wrap.remove();
37811         }
37812         Roo.form.TriggerField.superclass.onDestroy.call(this);
37813     },
37814
37815     // private
37816     onFocus : function(){
37817         Roo.form.TriggerField.superclass.onFocus.call(this);
37818         if(!this.mimicing){
37819             this.wrap.addClass('x-trigger-wrap-focus');
37820             this.mimicing = true;
37821             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37822             if(this.monitorTab){
37823                 this.el.on("keydown", this.checkTab, this);
37824             }
37825         }
37826     },
37827
37828     // private
37829     checkTab : function(e){
37830         if(e.getKey() == e.TAB){
37831             this.triggerBlur();
37832         }
37833     },
37834
37835     // private
37836     onBlur : function(){
37837         // do nothing
37838     },
37839
37840     // private
37841     mimicBlur : function(e, t){
37842         if(!this.wrap.contains(t) && this.validateBlur()){
37843             this.triggerBlur();
37844         }
37845     },
37846
37847     // private
37848     triggerBlur : function(){
37849         this.mimicing = false;
37850         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37851         if(this.monitorTab){
37852             this.el.un("keydown", this.checkTab, this);
37853         }
37854         this.wrap.removeClass('x-trigger-wrap-focus');
37855         Roo.form.TriggerField.superclass.onBlur.call(this);
37856     },
37857
37858     // private
37859     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37860     validateBlur : function(e, t){
37861         return true;
37862     },
37863
37864     // private
37865     onDisable : function(){
37866         Roo.form.TriggerField.superclass.onDisable.call(this);
37867         if(this.wrap){
37868             this.wrap.addClass('x-item-disabled');
37869         }
37870     },
37871
37872     // private
37873     onEnable : function(){
37874         Roo.form.TriggerField.superclass.onEnable.call(this);
37875         if(this.wrap){
37876             this.wrap.removeClass('x-item-disabled');
37877         }
37878     },
37879
37880     // private
37881     onShow : function(){
37882         var ae = this.getActionEl();
37883         
37884         if(ae){
37885             ae.dom.style.display = '';
37886             ae.dom.style.visibility = 'visible';
37887         }
37888     },
37889
37890     // private
37891     
37892     onHide : function(){
37893         var ae = this.getActionEl();
37894         ae.dom.style.display = 'none';
37895     },
37896
37897     /**
37898      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37899      * by an implementing function.
37900      * @method
37901      * @param {EventObject} e
37902      */
37903     onTriggerClick : Roo.emptyFn
37904 });
37905
37906 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37907 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37908 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37909 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37910     initComponent : function(){
37911         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37912
37913         this.triggerConfig = {
37914             tag:'span', cls:'x-form-twin-triggers', cn:[
37915             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37916             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37917         ]};
37918     },
37919
37920     getTrigger : function(index){
37921         return this.triggers[index];
37922     },
37923
37924     initTrigger : function(){
37925         var ts = this.trigger.select('.x-form-trigger', true);
37926         this.wrap.setStyle('overflow', 'hidden');
37927         var triggerField = this;
37928         ts.each(function(t, all, index){
37929             t.hide = function(){
37930                 var w = triggerField.wrap.getWidth();
37931                 this.dom.style.display = 'none';
37932                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37933             };
37934             t.show = function(){
37935                 var w = triggerField.wrap.getWidth();
37936                 this.dom.style.display = '';
37937                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37938             };
37939             var triggerIndex = 'Trigger'+(index+1);
37940
37941             if(this['hide'+triggerIndex]){
37942                 t.dom.style.display = 'none';
37943             }
37944             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37945             t.addClassOnOver('x-form-trigger-over');
37946             t.addClassOnClick('x-form-trigger-click');
37947         }, this);
37948         this.triggers = ts.elements;
37949     },
37950
37951     onTrigger1Click : Roo.emptyFn,
37952     onTrigger2Click : Roo.emptyFn
37953 });/*
37954  * Based on:
37955  * Ext JS Library 1.1.1
37956  * Copyright(c) 2006-2007, Ext JS, LLC.
37957  *
37958  * Originally Released Under LGPL - original licence link has changed is not relivant.
37959  *
37960  * Fork - LGPL
37961  * <script type="text/javascript">
37962  */
37963  
37964 /**
37965  * @class Roo.form.TextArea
37966  * @extends Roo.form.TextField
37967  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37968  * support for auto-sizing.
37969  * @constructor
37970  * Creates a new TextArea
37971  * @param {Object} config Configuration options
37972  */
37973 Roo.form.TextArea = function(config){
37974     Roo.form.TextArea.superclass.constructor.call(this, config);
37975     // these are provided exchanges for backwards compat
37976     // minHeight/maxHeight were replaced by growMin/growMax to be
37977     // compatible with TextField growing config values
37978     if(this.minHeight !== undefined){
37979         this.growMin = this.minHeight;
37980     }
37981     if(this.maxHeight !== undefined){
37982         this.growMax = this.maxHeight;
37983     }
37984 };
37985
37986 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37987     /**
37988      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37989      */
37990     growMin : 60,
37991     /**
37992      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37993      */
37994     growMax: 1000,
37995     /**
37996      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37997      * in the field (equivalent to setting overflow: hidden, defaults to false)
37998      */
37999     preventScrollbars: false,
38000     /**
38001      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38002      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38003      */
38004
38005     // private
38006     onRender : function(ct, position){
38007         if(!this.el){
38008             this.defaultAutoCreate = {
38009                 tag: "textarea",
38010                 style:"width:300px;height:60px;",
38011                 autocomplete: "off"
38012             };
38013         }
38014         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38015         if(this.grow){
38016             this.textSizeEl = Roo.DomHelper.append(document.body, {
38017                 tag: "pre", cls: "x-form-grow-sizer"
38018             });
38019             if(this.preventScrollbars){
38020                 this.el.setStyle("overflow", "hidden");
38021             }
38022             this.el.setHeight(this.growMin);
38023         }
38024     },
38025
38026     onDestroy : function(){
38027         if(this.textSizeEl){
38028             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38029         }
38030         Roo.form.TextArea.superclass.onDestroy.call(this);
38031     },
38032
38033     // private
38034     onKeyUp : function(e){
38035         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38036             this.autoSize();
38037         }
38038     },
38039
38040     /**
38041      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38042      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38043      */
38044     autoSize : function(){
38045         if(!this.grow || !this.textSizeEl){
38046             return;
38047         }
38048         var el = this.el;
38049         var v = el.dom.value;
38050         var ts = this.textSizeEl;
38051
38052         ts.innerHTML = '';
38053         ts.appendChild(document.createTextNode(v));
38054         v = ts.innerHTML;
38055
38056         Roo.fly(ts).setWidth(this.el.getWidth());
38057         if(v.length < 1){
38058             v = "&#160;&#160;";
38059         }else{
38060             if(Roo.isIE){
38061                 v = v.replace(/\n/g, '<p>&#160;</p>');
38062             }
38063             v += "&#160;\n&#160;";
38064         }
38065         ts.innerHTML = v;
38066         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38067         if(h != this.lastHeight){
38068             this.lastHeight = h;
38069             this.el.setHeight(h);
38070             this.fireEvent("autosize", this, h);
38071         }
38072     }
38073 });/*
38074  * Based on:
38075  * Ext JS Library 1.1.1
38076  * Copyright(c) 2006-2007, Ext JS, LLC.
38077  *
38078  * Originally Released Under LGPL - original licence link has changed is not relivant.
38079  *
38080  * Fork - LGPL
38081  * <script type="text/javascript">
38082  */
38083  
38084
38085 /**
38086  * @class Roo.form.NumberField
38087  * @extends Roo.form.TextField
38088  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38089  * @constructor
38090  * Creates a new NumberField
38091  * @param {Object} config Configuration options
38092  */
38093 Roo.form.NumberField = function(config){
38094     Roo.form.NumberField.superclass.constructor.call(this, config);
38095 };
38096
38097 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38098     /**
38099      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38100      */
38101     fieldClass: "x-form-field x-form-num-field",
38102     /**
38103      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38104      */
38105     allowDecimals : true,
38106     /**
38107      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38108      */
38109     decimalSeparator : ".",
38110     /**
38111      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38112      */
38113     decimalPrecision : 2,
38114     /**
38115      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38116      */
38117     allowNegative : true,
38118     /**
38119      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38120      */
38121     minValue : Number.NEGATIVE_INFINITY,
38122     /**
38123      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38124      */
38125     maxValue : Number.MAX_VALUE,
38126     /**
38127      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38128      */
38129     minText : "The minimum value for this field is {0}",
38130     /**
38131      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38132      */
38133     maxText : "The maximum value for this field is {0}",
38134     /**
38135      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38136      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38137      */
38138     nanText : "{0} is not a valid number",
38139
38140     // private
38141     initEvents : function(){
38142         Roo.form.NumberField.superclass.initEvents.call(this);
38143         var allowed = "0123456789";
38144         if(this.allowDecimals){
38145             allowed += this.decimalSeparator;
38146         }
38147         if(this.allowNegative){
38148             allowed += "-";
38149         }
38150         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38151         var keyPress = function(e){
38152             var k = e.getKey();
38153             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38154                 return;
38155             }
38156             var c = e.getCharCode();
38157             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38158                 e.stopEvent();
38159             }
38160         };
38161         this.el.on("keypress", keyPress, this);
38162     },
38163
38164     // private
38165     validateValue : function(value){
38166         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38167             return false;
38168         }
38169         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38170              return true;
38171         }
38172         var num = this.parseValue(value);
38173         if(isNaN(num)){
38174             this.markInvalid(String.format(this.nanText, value));
38175             return false;
38176         }
38177         if(num < this.minValue){
38178             this.markInvalid(String.format(this.minText, this.minValue));
38179             return false;
38180         }
38181         if(num > this.maxValue){
38182             this.markInvalid(String.format(this.maxText, this.maxValue));
38183             return false;
38184         }
38185         return true;
38186     },
38187
38188     getValue : function(){
38189         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38190     },
38191
38192     // private
38193     parseValue : function(value){
38194         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38195         return isNaN(value) ? '' : value;
38196     },
38197
38198     // private
38199     fixPrecision : function(value){
38200         var nan = isNaN(value);
38201         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38202             return nan ? '' : value;
38203         }
38204         return parseFloat(value).toFixed(this.decimalPrecision);
38205     },
38206
38207     setValue : function(v){
38208         v = this.fixPrecision(v);
38209         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38210     },
38211
38212     // private
38213     decimalPrecisionFcn : function(v){
38214         return Math.floor(v);
38215     },
38216
38217     beforeBlur : function(){
38218         var v = this.parseValue(this.getRawValue());
38219         if(v){
38220             this.setValue(v);
38221         }
38222     }
38223 });/*
38224  * Based on:
38225  * Ext JS Library 1.1.1
38226  * Copyright(c) 2006-2007, Ext JS, LLC.
38227  *
38228  * Originally Released Under LGPL - original licence link has changed is not relivant.
38229  *
38230  * Fork - LGPL
38231  * <script type="text/javascript">
38232  */
38233  
38234 /**
38235  * @class Roo.form.DateField
38236  * @extends Roo.form.TriggerField
38237  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38238 * @constructor
38239 * Create a new DateField
38240 * @param {Object} config
38241  */
38242 Roo.form.DateField = function(config){
38243     Roo.form.DateField.superclass.constructor.call(this, config);
38244     
38245       this.addEvents({
38246          
38247         /**
38248          * @event select
38249          * Fires when a date is selected
38250              * @param {Roo.form.DateField} combo This combo box
38251              * @param {Date} date The date selected
38252              */
38253         'select' : true
38254          
38255     });
38256     
38257     
38258     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38259     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38260     this.ddMatch = null;
38261     if(this.disabledDates){
38262         var dd = this.disabledDates;
38263         var re = "(?:";
38264         for(var i = 0; i < dd.length; i++){
38265             re += dd[i];
38266             if(i != dd.length-1) re += "|";
38267         }
38268         this.ddMatch = new RegExp(re + ")");
38269     }
38270 };
38271
38272 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38273     /**
38274      * @cfg {String} format
38275      * The default date format string which can be overriden for localization support.  The format must be
38276      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38277      */
38278     format : "m/d/y",
38279     /**
38280      * @cfg {String} altFormats
38281      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38282      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38283      */
38284     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38285     /**
38286      * @cfg {Array} disabledDays
38287      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38288      */
38289     disabledDays : null,
38290     /**
38291      * @cfg {String} disabledDaysText
38292      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38293      */
38294     disabledDaysText : "Disabled",
38295     /**
38296      * @cfg {Array} disabledDates
38297      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38298      * expression so they are very powerful. Some examples:
38299      * <ul>
38300      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38301      * <li>["03/08", "09/16"] would disable those days for every year</li>
38302      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38303      * <li>["03/../2006"] would disable every day in March 2006</li>
38304      * <li>["^03"] would disable every day in every March</li>
38305      * </ul>
38306      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38307      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38308      */
38309     disabledDates : null,
38310     /**
38311      * @cfg {String} disabledDatesText
38312      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38313      */
38314     disabledDatesText : "Disabled",
38315     /**
38316      * @cfg {Date/String} minValue
38317      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38318      * valid format (defaults to null).
38319      */
38320     minValue : null,
38321     /**
38322      * @cfg {Date/String} maxValue
38323      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38324      * valid format (defaults to null).
38325      */
38326     maxValue : null,
38327     /**
38328      * @cfg {String} minText
38329      * The error text to display when the date in the cell is before minValue (defaults to
38330      * 'The date in this field must be after {minValue}').
38331      */
38332     minText : "The date in this field must be equal to or after {0}",
38333     /**
38334      * @cfg {String} maxText
38335      * The error text to display when the date in the cell is after maxValue (defaults to
38336      * 'The date in this field must be before {maxValue}').
38337      */
38338     maxText : "The date in this field must be equal to or before {0}",
38339     /**
38340      * @cfg {String} invalidText
38341      * The error text to display when the date in the field is invalid (defaults to
38342      * '{value} is not a valid date - it must be in the format {format}').
38343      */
38344     invalidText : "{0} is not a valid date - it must be in the format {1}",
38345     /**
38346      * @cfg {String} triggerClass
38347      * An additional CSS class used to style the trigger button.  The trigger will always get the
38348      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38349      * which displays a calendar icon).
38350      */
38351     triggerClass : 'x-form-date-trigger',
38352     
38353
38354     /**
38355      * @cfg {Boolean} useIso
38356      * if enabled, then the date field will use a hidden field to store the 
38357      * real value as iso formated date. default (false)
38358      */ 
38359     useIso : false,
38360     /**
38361      * @cfg {String/Object} autoCreate
38362      * A DomHelper element spec, or true for a default element spec (defaults to
38363      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38364      */ 
38365     // private
38366     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38367     
38368     // private
38369     hiddenField: false,
38370     
38371     onRender : function(ct, position)
38372     {
38373         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38374         if (this.useIso) {
38375             //this.el.dom.removeAttribute('name'); 
38376             Roo.log("Changing name?");
38377             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38378             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38379                     'before', true);
38380             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38381             // prevent input submission
38382             this.hiddenName = this.name;
38383         }
38384             
38385             
38386     },
38387     
38388     // private
38389     validateValue : function(value)
38390     {
38391         value = this.formatDate(value);
38392         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38393             Roo.log('super failed');
38394             return false;
38395         }
38396         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38397              return true;
38398         }
38399         var svalue = value;
38400         value = this.parseDate(value);
38401         if(!value){
38402             Roo.log('parse date failed' + svalue);
38403             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38404             return false;
38405         }
38406         var time = value.getTime();
38407         if(this.minValue && time < this.minValue.getTime()){
38408             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38409             return false;
38410         }
38411         if(this.maxValue && time > this.maxValue.getTime()){
38412             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38413             return false;
38414         }
38415         if(this.disabledDays){
38416             var day = value.getDay();
38417             for(var i = 0; i < this.disabledDays.length; i++) {
38418                 if(day === this.disabledDays[i]){
38419                     this.markInvalid(this.disabledDaysText);
38420                     return false;
38421                 }
38422             }
38423         }
38424         var fvalue = this.formatDate(value);
38425         if(this.ddMatch && this.ddMatch.test(fvalue)){
38426             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38427             return false;
38428         }
38429         return true;
38430     },
38431
38432     // private
38433     // Provides logic to override the default TriggerField.validateBlur which just returns true
38434     validateBlur : function(){
38435         return !this.menu || !this.menu.isVisible();
38436     },
38437     
38438     getName: function()
38439     {
38440         // returns hidden if it's set..
38441         if (!this.rendered) {return ''};
38442         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38443         
38444     },
38445
38446     /**
38447      * Returns the current date value of the date field.
38448      * @return {Date} The date value
38449      */
38450     getValue : function(){
38451         
38452         return  this.hiddenField ?
38453                 this.hiddenField.value :
38454                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38455     },
38456
38457     /**
38458      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38459      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38460      * (the default format used is "m/d/y").
38461      * <br />Usage:
38462      * <pre><code>
38463 //All of these calls set the same date value (May 4, 2006)
38464
38465 //Pass a date object:
38466 var dt = new Date('5/4/06');
38467 dateField.setValue(dt);
38468
38469 //Pass a date string (default format):
38470 dateField.setValue('5/4/06');
38471
38472 //Pass a date string (custom format):
38473 dateField.format = 'Y-m-d';
38474 dateField.setValue('2006-5-4');
38475 </code></pre>
38476      * @param {String/Date} date The date or valid date string
38477      */
38478     setValue : function(date){
38479         if (this.hiddenField) {
38480             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38481         }
38482         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38483         // make sure the value field is always stored as a date..
38484         this.value = this.parseDate(date);
38485         
38486         
38487     },
38488
38489     // private
38490     parseDate : function(value){
38491         if(!value || value instanceof Date){
38492             return value;
38493         }
38494         var v = Date.parseDate(value, this.format);
38495          if (!v && this.useIso) {
38496             v = Date.parseDate(value, 'Y-m-d');
38497         }
38498         if(!v && this.altFormats){
38499             if(!this.altFormatsArray){
38500                 this.altFormatsArray = this.altFormats.split("|");
38501             }
38502             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38503                 v = Date.parseDate(value, this.altFormatsArray[i]);
38504             }
38505         }
38506         return v;
38507     },
38508
38509     // private
38510     formatDate : function(date, fmt){
38511         return (!date || !(date instanceof Date)) ?
38512                date : date.dateFormat(fmt || this.format);
38513     },
38514
38515     // private
38516     menuListeners : {
38517         select: function(m, d){
38518             
38519             this.setValue(d);
38520             this.fireEvent('select', this, d);
38521         },
38522         show : function(){ // retain focus styling
38523             this.onFocus();
38524         },
38525         hide : function(){
38526             this.focus.defer(10, this);
38527             var ml = this.menuListeners;
38528             this.menu.un("select", ml.select,  this);
38529             this.menu.un("show", ml.show,  this);
38530             this.menu.un("hide", ml.hide,  this);
38531         }
38532     },
38533
38534     // private
38535     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38536     onTriggerClick : function(){
38537         if(this.disabled){
38538             return;
38539         }
38540         if(this.menu == null){
38541             this.menu = new Roo.menu.DateMenu();
38542         }
38543         Roo.apply(this.menu.picker,  {
38544             showClear: this.allowBlank,
38545             minDate : this.minValue,
38546             maxDate : this.maxValue,
38547             disabledDatesRE : this.ddMatch,
38548             disabledDatesText : this.disabledDatesText,
38549             disabledDays : this.disabledDays,
38550             disabledDaysText : this.disabledDaysText,
38551             format : this.useIso ? 'Y-m-d' : this.format,
38552             minText : String.format(this.minText, this.formatDate(this.minValue)),
38553             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38554         });
38555         this.menu.on(Roo.apply({}, this.menuListeners, {
38556             scope:this
38557         }));
38558         this.menu.picker.setValue(this.getValue() || new Date());
38559         this.menu.show(this.el, "tl-bl?");
38560     },
38561
38562     beforeBlur : function(){
38563         var v = this.parseDate(this.getRawValue());
38564         if(v){
38565             this.setValue(v);
38566         }
38567     }
38568
38569     /** @cfg {Boolean} grow @hide */
38570     /** @cfg {Number} growMin @hide */
38571     /** @cfg {Number} growMax @hide */
38572     /**
38573      * @hide
38574      * @method autoSize
38575      */
38576 });/*
38577  * Based on:
38578  * Ext JS Library 1.1.1
38579  * Copyright(c) 2006-2007, Ext JS, LLC.
38580  *
38581  * Originally Released Under LGPL - original licence link has changed is not relivant.
38582  *
38583  * Fork - LGPL
38584  * <script type="text/javascript">
38585  */
38586  
38587 /**
38588  * @class Roo.form.MonthField
38589  * @extends Roo.form.TriggerField
38590  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38591 * @constructor
38592 * Create a new MonthField
38593 * @param {Object} config
38594  */
38595 Roo.form.MonthField = function(config){
38596     
38597     Roo.form.MonthField.superclass.constructor.call(this, config);
38598     
38599       this.addEvents({
38600          
38601         /**
38602          * @event select
38603          * Fires when a date is selected
38604              * @param {Roo.form.MonthFieeld} combo This combo box
38605              * @param {Date} date The date selected
38606              */
38607         'select' : true
38608          
38609     });
38610     
38611     
38612     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38613     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38614     this.ddMatch = null;
38615     if(this.disabledDates){
38616         var dd = this.disabledDates;
38617         var re = "(?:";
38618         for(var i = 0; i < dd.length; i++){
38619             re += dd[i];
38620             if(i != dd.length-1) re += "|";
38621         }
38622         this.ddMatch = new RegExp(re + ")");
38623     }
38624 };
38625
38626 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38627     /**
38628      * @cfg {String} format
38629      * The default date format string which can be overriden for localization support.  The format must be
38630      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38631      */
38632     format : "M Y",
38633     /**
38634      * @cfg {String} altFormats
38635      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38636      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38637      */
38638     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38639     /**
38640      * @cfg {Array} disabledDays
38641      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38642      */
38643     disabledDays : [0,1,2,3,4,5,6],
38644     /**
38645      * @cfg {String} disabledDaysText
38646      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38647      */
38648     disabledDaysText : "Disabled",
38649     /**
38650      * @cfg {Array} disabledDates
38651      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38652      * expression so they are very powerful. Some examples:
38653      * <ul>
38654      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38655      * <li>["03/08", "09/16"] would disable those days for every year</li>
38656      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38657      * <li>["03/../2006"] would disable every day in March 2006</li>
38658      * <li>["^03"] would disable every day in every March</li>
38659      * </ul>
38660      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38661      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38662      */
38663     disabledDates : null,
38664     /**
38665      * @cfg {String} disabledDatesText
38666      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38667      */
38668     disabledDatesText : "Disabled",
38669     /**
38670      * @cfg {Date/String} minValue
38671      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38672      * valid format (defaults to null).
38673      */
38674     minValue : null,
38675     /**
38676      * @cfg {Date/String} maxValue
38677      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38678      * valid format (defaults to null).
38679      */
38680     maxValue : null,
38681     /**
38682      * @cfg {String} minText
38683      * The error text to display when the date in the cell is before minValue (defaults to
38684      * 'The date in this field must be after {minValue}').
38685      */
38686     minText : "The date in this field must be equal to or after {0}",
38687     /**
38688      * @cfg {String} maxTextf
38689      * The error text to display when the date in the cell is after maxValue (defaults to
38690      * 'The date in this field must be before {maxValue}').
38691      */
38692     maxText : "The date in this field must be equal to or before {0}",
38693     /**
38694      * @cfg {String} invalidText
38695      * The error text to display when the date in the field is invalid (defaults to
38696      * '{value} is not a valid date - it must be in the format {format}').
38697      */
38698     invalidText : "{0} is not a valid date - it must be in the format {1}",
38699     /**
38700      * @cfg {String} triggerClass
38701      * An additional CSS class used to style the trigger button.  The trigger will always get the
38702      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38703      * which displays a calendar icon).
38704      */
38705     triggerClass : 'x-form-date-trigger',
38706     
38707
38708     /**
38709      * @cfg {Boolean} useIso
38710      * if enabled, then the date field will use a hidden field to store the 
38711      * real value as iso formated date. default (true)
38712      */ 
38713     useIso : true,
38714     /**
38715      * @cfg {String/Object} autoCreate
38716      * A DomHelper element spec, or true for a default element spec (defaults to
38717      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38718      */ 
38719     // private
38720     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38721     
38722     // private
38723     hiddenField: false,
38724     
38725     hideMonthPicker : false,
38726     
38727     onRender : function(ct, position)
38728     {
38729         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38730         if (this.useIso) {
38731             this.el.dom.removeAttribute('name'); 
38732             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38733                     'before', true);
38734             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38735             // prevent input submission
38736             this.hiddenName = this.name;
38737         }
38738             
38739             
38740     },
38741     
38742     // private
38743     validateValue : function(value)
38744     {
38745         value = this.formatDate(value);
38746         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38747             return false;
38748         }
38749         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38750              return true;
38751         }
38752         var svalue = value;
38753         value = this.parseDate(value);
38754         if(!value){
38755             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38756             return false;
38757         }
38758         var time = value.getTime();
38759         if(this.minValue && time < this.minValue.getTime()){
38760             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38761             return false;
38762         }
38763         if(this.maxValue && time > this.maxValue.getTime()){
38764             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38765             return false;
38766         }
38767         /*if(this.disabledDays){
38768             var day = value.getDay();
38769             for(var i = 0; i < this.disabledDays.length; i++) {
38770                 if(day === this.disabledDays[i]){
38771                     this.markInvalid(this.disabledDaysText);
38772                     return false;
38773                 }
38774             }
38775         }
38776         */
38777         var fvalue = this.formatDate(value);
38778         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38779             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38780             return false;
38781         }
38782         */
38783         return true;
38784     },
38785
38786     // private
38787     // Provides logic to override the default TriggerField.validateBlur which just returns true
38788     validateBlur : function(){
38789         return !this.menu || !this.menu.isVisible();
38790     },
38791
38792     /**
38793      * Returns the current date value of the date field.
38794      * @return {Date} The date value
38795      */
38796     getValue : function(){
38797         
38798         
38799         
38800         return  this.hiddenField ?
38801                 this.hiddenField.value :
38802                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38803     },
38804
38805     /**
38806      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38807      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38808      * (the default format used is "m/d/y").
38809      * <br />Usage:
38810      * <pre><code>
38811 //All of these calls set the same date value (May 4, 2006)
38812
38813 //Pass a date object:
38814 var dt = new Date('5/4/06');
38815 monthField.setValue(dt);
38816
38817 //Pass a date string (default format):
38818 monthField.setValue('5/4/06');
38819
38820 //Pass a date string (custom format):
38821 monthField.format = 'Y-m-d';
38822 monthField.setValue('2006-5-4');
38823 </code></pre>
38824      * @param {String/Date} date The date or valid date string
38825      */
38826     setValue : function(date){
38827         Roo.log('month setValue' + date);
38828         // can only be first of month..
38829         
38830         var val = this.parseDate(date);
38831         
38832         if (this.hiddenField) {
38833             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38834         }
38835         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38836         this.value = this.parseDate(date);
38837     },
38838
38839     // private
38840     parseDate : function(value){
38841         if(!value || value instanceof Date){
38842             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38843             return value;
38844         }
38845         var v = Date.parseDate(value, this.format);
38846         if (!v && this.useIso) {
38847             v = Date.parseDate(value, 'Y-m-d');
38848         }
38849         if (v) {
38850             // 
38851             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38852         }
38853         
38854         
38855         if(!v && this.altFormats){
38856             if(!this.altFormatsArray){
38857                 this.altFormatsArray = this.altFormats.split("|");
38858             }
38859             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38860                 v = Date.parseDate(value, this.altFormatsArray[i]);
38861             }
38862         }
38863         return v;
38864     },
38865
38866     // private
38867     formatDate : function(date, fmt){
38868         return (!date || !(date instanceof Date)) ?
38869                date : date.dateFormat(fmt || this.format);
38870     },
38871
38872     // private
38873     menuListeners : {
38874         select: function(m, d){
38875             this.setValue(d);
38876             this.fireEvent('select', this, d);
38877         },
38878         show : function(){ // retain focus styling
38879             this.onFocus();
38880         },
38881         hide : function(){
38882             this.focus.defer(10, this);
38883             var ml = this.menuListeners;
38884             this.menu.un("select", ml.select,  this);
38885             this.menu.un("show", ml.show,  this);
38886             this.menu.un("hide", ml.hide,  this);
38887         }
38888     },
38889     // private
38890     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38891     onTriggerClick : function(){
38892         if(this.disabled){
38893             return;
38894         }
38895         if(this.menu == null){
38896             this.menu = new Roo.menu.DateMenu();
38897            
38898         }
38899         
38900         Roo.apply(this.menu.picker,  {
38901             
38902             showClear: this.allowBlank,
38903             minDate : this.minValue,
38904             maxDate : this.maxValue,
38905             disabledDatesRE : this.ddMatch,
38906             disabledDatesText : this.disabledDatesText,
38907             
38908             format : this.useIso ? 'Y-m-d' : this.format,
38909             minText : String.format(this.minText, this.formatDate(this.minValue)),
38910             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38911             
38912         });
38913          this.menu.on(Roo.apply({}, this.menuListeners, {
38914             scope:this
38915         }));
38916        
38917         
38918         var m = this.menu;
38919         var p = m.picker;
38920         
38921         // hide month picker get's called when we called by 'before hide';
38922         
38923         var ignorehide = true;
38924         p.hideMonthPicker  = function(disableAnim){
38925             if (ignorehide) {
38926                 return;
38927             }
38928              if(this.monthPicker){
38929                 Roo.log("hideMonthPicker called");
38930                 if(disableAnim === true){
38931                     this.monthPicker.hide();
38932                 }else{
38933                     this.monthPicker.slideOut('t', {duration:.2});
38934                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38935                     p.fireEvent("select", this, this.value);
38936                     m.hide();
38937                 }
38938             }
38939         }
38940         
38941         Roo.log('picker set value');
38942         Roo.log(this.getValue());
38943         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38944         m.show(this.el, 'tl-bl?');
38945         ignorehide  = false;
38946         // this will trigger hideMonthPicker..
38947         
38948         
38949         // hidden the day picker
38950         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38951         
38952         
38953         
38954       
38955         
38956         p.showMonthPicker.defer(100, p);
38957     
38958         
38959        
38960     },
38961
38962     beforeBlur : function(){
38963         var v = this.parseDate(this.getRawValue());
38964         if(v){
38965             this.setValue(v);
38966         }
38967     }
38968
38969     /** @cfg {Boolean} grow @hide */
38970     /** @cfg {Number} growMin @hide */
38971     /** @cfg {Number} growMax @hide */
38972     /**
38973      * @hide
38974      * @method autoSize
38975      */
38976 });/*
38977  * Based on:
38978  * Ext JS Library 1.1.1
38979  * Copyright(c) 2006-2007, Ext JS, LLC.
38980  *
38981  * Originally Released Under LGPL - original licence link has changed is not relivant.
38982  *
38983  * Fork - LGPL
38984  * <script type="text/javascript">
38985  */
38986  
38987
38988 /**
38989  * @class Roo.form.ComboBox
38990  * @extends Roo.form.TriggerField
38991  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38992  * @constructor
38993  * Create a new ComboBox.
38994  * @param {Object} config Configuration options
38995  */
38996 Roo.form.ComboBox = function(config){
38997     Roo.form.ComboBox.superclass.constructor.call(this, config);
38998     this.addEvents({
38999         /**
39000          * @event expand
39001          * Fires when the dropdown list is expanded
39002              * @param {Roo.form.ComboBox} combo This combo box
39003              */
39004         'expand' : true,
39005         /**
39006          * @event collapse
39007          * Fires when the dropdown list is collapsed
39008              * @param {Roo.form.ComboBox} combo This combo box
39009              */
39010         'collapse' : true,
39011         /**
39012          * @event beforeselect
39013          * Fires before a list item is selected. Return false to cancel the selection.
39014              * @param {Roo.form.ComboBox} combo This combo box
39015              * @param {Roo.data.Record} record The data record returned from the underlying store
39016              * @param {Number} index The index of the selected item in the dropdown list
39017              */
39018         'beforeselect' : true,
39019         /**
39020          * @event select
39021          * Fires when a list item is selected
39022              * @param {Roo.form.ComboBox} combo This combo box
39023              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39024              * @param {Number} index The index of the selected item in the dropdown list
39025              */
39026         'select' : true,
39027         /**
39028          * @event beforequery
39029          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39030          * The event object passed has these properties:
39031              * @param {Roo.form.ComboBox} combo This combo box
39032              * @param {String} query The query
39033              * @param {Boolean} forceAll true to force "all" query
39034              * @param {Boolean} cancel true to cancel the query
39035              * @param {Object} e The query event object
39036              */
39037         'beforequery': true,
39038          /**
39039          * @event add
39040          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39041              * @param {Roo.form.ComboBox} combo This combo box
39042              */
39043         'add' : true,
39044         /**
39045          * @event edit
39046          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39047              * @param {Roo.form.ComboBox} combo This combo box
39048              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39049              */
39050         'edit' : true
39051         
39052         
39053     });
39054     if(this.transform){
39055         this.allowDomMove = false;
39056         var s = Roo.getDom(this.transform);
39057         if(!this.hiddenName){
39058             this.hiddenName = s.name;
39059         }
39060         if(!this.store){
39061             this.mode = 'local';
39062             var d = [], opts = s.options;
39063             for(var i = 0, len = opts.length;i < len; i++){
39064                 var o = opts[i];
39065                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39066                 if(o.selected) {
39067                     this.value = value;
39068                 }
39069                 d.push([value, o.text]);
39070             }
39071             this.store = new Roo.data.SimpleStore({
39072                 'id': 0,
39073                 fields: ['value', 'text'],
39074                 data : d
39075             });
39076             this.valueField = 'value';
39077             this.displayField = 'text';
39078         }
39079         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39080         if(!this.lazyRender){
39081             this.target = true;
39082             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39083             s.parentNode.removeChild(s); // remove it
39084             this.render(this.el.parentNode);
39085         }else{
39086             s.parentNode.removeChild(s); // remove it
39087         }
39088
39089     }
39090     if (this.store) {
39091         this.store = Roo.factory(this.store, Roo.data);
39092     }
39093     
39094     this.selectedIndex = -1;
39095     if(this.mode == 'local'){
39096         if(config.queryDelay === undefined){
39097             this.queryDelay = 10;
39098         }
39099         if(config.minChars === undefined){
39100             this.minChars = 0;
39101         }
39102     }
39103 };
39104
39105 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39106     /**
39107      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39108      */
39109     /**
39110      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39111      * rendering into an Roo.Editor, defaults to false)
39112      */
39113     /**
39114      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39115      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39116      */
39117     /**
39118      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39119      */
39120     /**
39121      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39122      * the dropdown list (defaults to undefined, with no header element)
39123      */
39124
39125      /**
39126      * @cfg {String/Roo.Template} tpl The template to use to render the output
39127      */
39128      
39129     // private
39130     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39131     /**
39132      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39133      */
39134     listWidth: undefined,
39135     /**
39136      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39137      * mode = 'remote' or 'text' if mode = 'local')
39138      */
39139     displayField: undefined,
39140     /**
39141      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39142      * mode = 'remote' or 'value' if mode = 'local'). 
39143      * Note: use of a valueField requires the user make a selection
39144      * in order for a value to be mapped.
39145      */
39146     valueField: undefined,
39147     
39148     
39149     /**
39150      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39151      * field's data value (defaults to the underlying DOM element's name)
39152      */
39153     hiddenName: undefined,
39154     /**
39155      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39156      */
39157     listClass: '',
39158     /**
39159      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39160      */
39161     selectedClass: 'x-combo-selected',
39162     /**
39163      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39164      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39165      * which displays a downward arrow icon).
39166      */
39167     triggerClass : 'x-form-arrow-trigger',
39168     /**
39169      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39170      */
39171     shadow:'sides',
39172     /**
39173      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39174      * anchor positions (defaults to 'tl-bl')
39175      */
39176     listAlign: 'tl-bl?',
39177     /**
39178      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39179      */
39180     maxHeight: 300,
39181     /**
39182      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39183      * query specified by the allQuery config option (defaults to 'query')
39184      */
39185     triggerAction: 'query',
39186     /**
39187      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39188      * (defaults to 4, does not apply if editable = false)
39189      */
39190     minChars : 4,
39191     /**
39192      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39193      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39194      */
39195     typeAhead: false,
39196     /**
39197      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39198      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39199      */
39200     queryDelay: 500,
39201     /**
39202      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39203      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39204      */
39205     pageSize: 0,
39206     /**
39207      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39208      * when editable = true (defaults to false)
39209      */
39210     selectOnFocus:false,
39211     /**
39212      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39213      */
39214     queryParam: 'query',
39215     /**
39216      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39217      * when mode = 'remote' (defaults to 'Loading...')
39218      */
39219     loadingText: 'Loading...',
39220     /**
39221      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39222      */
39223     resizable: false,
39224     /**
39225      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39226      */
39227     handleHeight : 8,
39228     /**
39229      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39230      * traditional select (defaults to true)
39231      */
39232     editable: true,
39233     /**
39234      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39235      */
39236     allQuery: '',
39237     /**
39238      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39239      */
39240     mode: 'remote',
39241     /**
39242      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39243      * listWidth has a higher value)
39244      */
39245     minListWidth : 70,
39246     /**
39247      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39248      * allow the user to set arbitrary text into the field (defaults to false)
39249      */
39250     forceSelection:false,
39251     /**
39252      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39253      * if typeAhead = true (defaults to 250)
39254      */
39255     typeAheadDelay : 250,
39256     /**
39257      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39258      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39259      */
39260     valueNotFoundText : undefined,
39261     /**
39262      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39263      */
39264     blockFocus : false,
39265     
39266     /**
39267      * @cfg {Boolean} disableClear Disable showing of clear button.
39268      */
39269     disableClear : false,
39270     /**
39271      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39272      */
39273     alwaysQuery : false,
39274     
39275     //private
39276     addicon : false,
39277     editicon: false,
39278     
39279     // element that contains real text value.. (when hidden is used..)
39280      
39281     // private
39282     onRender : function(ct, position){
39283         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39284         if(this.hiddenName){
39285             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39286                     'before', true);
39287             this.hiddenField.value =
39288                 this.hiddenValue !== undefined ? this.hiddenValue :
39289                 this.value !== undefined ? this.value : '';
39290
39291             // prevent input submission
39292             this.el.dom.removeAttribute('name');
39293              
39294              
39295         }
39296         if(Roo.isGecko){
39297             this.el.dom.setAttribute('autocomplete', 'off');
39298         }
39299
39300         var cls = 'x-combo-list';
39301
39302         this.list = new Roo.Layer({
39303             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39304         });
39305
39306         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39307         this.list.setWidth(lw);
39308         this.list.swallowEvent('mousewheel');
39309         this.assetHeight = 0;
39310
39311         if(this.title){
39312             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39313             this.assetHeight += this.header.getHeight();
39314         }
39315
39316         this.innerList = this.list.createChild({cls:cls+'-inner'});
39317         this.innerList.on('mouseover', this.onViewOver, this);
39318         this.innerList.on('mousemove', this.onViewMove, this);
39319         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39320         
39321         if(this.allowBlank && !this.pageSize && !this.disableClear){
39322             this.footer = this.list.createChild({cls:cls+'-ft'});
39323             this.pageTb = new Roo.Toolbar(this.footer);
39324            
39325         }
39326         if(this.pageSize){
39327             this.footer = this.list.createChild({cls:cls+'-ft'});
39328             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39329                     {pageSize: this.pageSize});
39330             
39331         }
39332         
39333         if (this.pageTb && this.allowBlank && !this.disableClear) {
39334             var _this = this;
39335             this.pageTb.add(new Roo.Toolbar.Fill(), {
39336                 cls: 'x-btn-icon x-btn-clear',
39337                 text: '&#160;',
39338                 handler: function()
39339                 {
39340                     _this.collapse();
39341                     _this.clearValue();
39342                     _this.onSelect(false, -1);
39343                 }
39344             });
39345         }
39346         if (this.footer) {
39347             this.assetHeight += this.footer.getHeight();
39348         }
39349         
39350
39351         if(!this.tpl){
39352             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39353         }
39354
39355         this.view = new Roo.View(this.innerList, this.tpl, {
39356             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39357         });
39358
39359         this.view.on('click', this.onViewClick, this);
39360
39361         this.store.on('beforeload', this.onBeforeLoad, this);
39362         this.store.on('load', this.onLoad, this);
39363         this.store.on('loadexception', this.onLoadException, this);
39364
39365         if(this.resizable){
39366             this.resizer = new Roo.Resizable(this.list,  {
39367                pinned:true, handles:'se'
39368             });
39369             this.resizer.on('resize', function(r, w, h){
39370                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39371                 this.listWidth = w;
39372                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39373                 this.restrictHeight();
39374             }, this);
39375             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39376         }
39377         if(!this.editable){
39378             this.editable = true;
39379             this.setEditable(false);
39380         }  
39381         
39382         
39383         if (typeof(this.events.add.listeners) != 'undefined') {
39384             
39385             this.addicon = this.wrap.createChild(
39386                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39387        
39388             this.addicon.on('click', function(e) {
39389                 this.fireEvent('add', this);
39390             }, this);
39391         }
39392         if (typeof(this.events.edit.listeners) != 'undefined') {
39393             
39394             this.editicon = this.wrap.createChild(
39395                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39396             if (this.addicon) {
39397                 this.editicon.setStyle('margin-left', '40px');
39398             }
39399             this.editicon.on('click', function(e) {
39400                 
39401                 // we fire even  if inothing is selected..
39402                 this.fireEvent('edit', this, this.lastData );
39403                 
39404             }, this);
39405         }
39406         
39407         
39408         
39409     },
39410
39411     // private
39412     initEvents : function(){
39413         Roo.form.ComboBox.superclass.initEvents.call(this);
39414
39415         this.keyNav = new Roo.KeyNav(this.el, {
39416             "up" : function(e){
39417                 this.inKeyMode = true;
39418                 this.selectPrev();
39419             },
39420
39421             "down" : function(e){
39422                 if(!this.isExpanded()){
39423                     this.onTriggerClick();
39424                 }else{
39425                     this.inKeyMode = true;
39426                     this.selectNext();
39427                 }
39428             },
39429
39430             "enter" : function(e){
39431                 this.onViewClick();
39432                 //return true;
39433             },
39434
39435             "esc" : function(e){
39436                 this.collapse();
39437             },
39438
39439             "tab" : function(e){
39440                 this.onViewClick(false);
39441                 this.fireEvent("specialkey", this, e);
39442                 return true;
39443             },
39444
39445             scope : this,
39446
39447             doRelay : function(foo, bar, hname){
39448                 if(hname == 'down' || this.scope.isExpanded()){
39449                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39450                 }
39451                 return true;
39452             },
39453
39454             forceKeyDown: true
39455         });
39456         this.queryDelay = Math.max(this.queryDelay || 10,
39457                 this.mode == 'local' ? 10 : 250);
39458         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39459         if(this.typeAhead){
39460             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39461         }
39462         if(this.editable !== false){
39463             this.el.on("keyup", this.onKeyUp, this);
39464         }
39465         if(this.forceSelection){
39466             this.on('blur', this.doForce, this);
39467         }
39468     },
39469
39470     onDestroy : function(){
39471         if(this.view){
39472             this.view.setStore(null);
39473             this.view.el.removeAllListeners();
39474             this.view.el.remove();
39475             this.view.purgeListeners();
39476         }
39477         if(this.list){
39478             this.list.destroy();
39479         }
39480         if(this.store){
39481             this.store.un('beforeload', this.onBeforeLoad, this);
39482             this.store.un('load', this.onLoad, this);
39483             this.store.un('loadexception', this.onLoadException, this);
39484         }
39485         Roo.form.ComboBox.superclass.onDestroy.call(this);
39486     },
39487
39488     // private
39489     fireKey : function(e){
39490         if(e.isNavKeyPress() && !this.list.isVisible()){
39491             this.fireEvent("specialkey", this, e);
39492         }
39493     },
39494
39495     // private
39496     onResize: function(w, h){
39497         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39498         
39499         if(typeof w != 'number'){
39500             // we do not handle it!?!?
39501             return;
39502         }
39503         var tw = this.trigger.getWidth();
39504         tw += this.addicon ? this.addicon.getWidth() : 0;
39505         tw += this.editicon ? this.editicon.getWidth() : 0;
39506         var x = w - tw;
39507         this.el.setWidth( this.adjustWidth('input', x));
39508             
39509         this.trigger.setStyle('left', x+'px');
39510         
39511         if(this.list && this.listWidth === undefined){
39512             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39513             this.list.setWidth(lw);
39514             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39515         }
39516         
39517     
39518         
39519     },
39520
39521     /**
39522      * Allow or prevent the user from directly editing the field text.  If false is passed,
39523      * the user will only be able to select from the items defined in the dropdown list.  This method
39524      * is the runtime equivalent of setting the 'editable' config option at config time.
39525      * @param {Boolean} value True to allow the user to directly edit the field text
39526      */
39527     setEditable : function(value){
39528         if(value == this.editable){
39529             return;
39530         }
39531         this.editable = value;
39532         if(!value){
39533             this.el.dom.setAttribute('readOnly', true);
39534             this.el.on('mousedown', this.onTriggerClick,  this);
39535             this.el.addClass('x-combo-noedit');
39536         }else{
39537             this.el.dom.setAttribute('readOnly', false);
39538             this.el.un('mousedown', this.onTriggerClick,  this);
39539             this.el.removeClass('x-combo-noedit');
39540         }
39541     },
39542
39543     // private
39544     onBeforeLoad : function(){
39545         if(!this.hasFocus){
39546             return;
39547         }
39548         this.innerList.update(this.loadingText ?
39549                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39550         this.restrictHeight();
39551         this.selectedIndex = -1;
39552     },
39553
39554     // private
39555     onLoad : function(){
39556         if(!this.hasFocus){
39557             return;
39558         }
39559         if(this.store.getCount() > 0){
39560             this.expand();
39561             this.restrictHeight();
39562             if(this.lastQuery == this.allQuery){
39563                 if(this.editable){
39564                     this.el.dom.select();
39565                 }
39566                 if(!this.selectByValue(this.value, true)){
39567                     this.select(0, true);
39568                 }
39569             }else{
39570                 this.selectNext();
39571                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39572                     this.taTask.delay(this.typeAheadDelay);
39573                 }
39574             }
39575         }else{
39576             this.onEmptyResults();
39577         }
39578         //this.el.focus();
39579     },
39580     // private
39581     onLoadException : function()
39582     {
39583         this.collapse();
39584         Roo.log(this.store.reader.jsonData);
39585         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39586             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39587         }
39588         
39589         
39590     },
39591     // private
39592     onTypeAhead : function(){
39593         if(this.store.getCount() > 0){
39594             var r = this.store.getAt(0);
39595             var newValue = r.data[this.displayField];
39596             var len = newValue.length;
39597             var selStart = this.getRawValue().length;
39598             if(selStart != len){
39599                 this.setRawValue(newValue);
39600                 this.selectText(selStart, newValue.length);
39601             }
39602         }
39603     },
39604
39605     // private
39606     onSelect : function(record, index){
39607         if(this.fireEvent('beforeselect', this, record, index) !== false){
39608             this.setFromData(index > -1 ? record.data : false);
39609             this.collapse();
39610             this.fireEvent('select', this, record, index);
39611         }
39612     },
39613
39614     /**
39615      * Returns the currently selected field value or empty string if no value is set.
39616      * @return {String} value The selected value
39617      */
39618     getValue : function(){
39619         if(this.valueField){
39620             return typeof this.value != 'undefined' ? this.value : '';
39621         }else{
39622             return Roo.form.ComboBox.superclass.getValue.call(this);
39623         }
39624     },
39625
39626     /**
39627      * Clears any text/value currently set in the field
39628      */
39629     clearValue : function(){
39630         if(this.hiddenField){
39631             this.hiddenField.value = '';
39632         }
39633         this.value = '';
39634         this.setRawValue('');
39635         this.lastSelectionText = '';
39636         
39637     },
39638
39639     /**
39640      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39641      * will be displayed in the field.  If the value does not match the data value of an existing item,
39642      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39643      * Otherwise the field will be blank (although the value will still be set).
39644      * @param {String} value The value to match
39645      */
39646     setValue : function(v){
39647         var text = v;
39648         if(this.valueField){
39649             var r = this.findRecord(this.valueField, v);
39650             if(r){
39651                 text = r.data[this.displayField];
39652             }else if(this.valueNotFoundText !== undefined){
39653                 text = this.valueNotFoundText;
39654             }
39655         }
39656         this.lastSelectionText = text;
39657         if(this.hiddenField){
39658             this.hiddenField.value = v;
39659         }
39660         Roo.form.ComboBox.superclass.setValue.call(this, text);
39661         this.value = v;
39662     },
39663     /**
39664      * @property {Object} the last set data for the element
39665      */
39666     
39667     lastData : false,
39668     /**
39669      * Sets the value of the field based on a object which is related to the record format for the store.
39670      * @param {Object} value the value to set as. or false on reset?
39671      */
39672     setFromData : function(o){
39673         var dv = ''; // display value
39674         var vv = ''; // value value..
39675         this.lastData = o;
39676         if (this.displayField) {
39677             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39678         } else {
39679             // this is an error condition!!!
39680             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39681         }
39682         
39683         if(this.valueField){
39684             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39685         }
39686         if(this.hiddenField){
39687             this.hiddenField.value = vv;
39688             
39689             this.lastSelectionText = dv;
39690             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39691             this.value = vv;
39692             return;
39693         }
39694         // no hidden field.. - we store the value in 'value', but still display
39695         // display field!!!!
39696         this.lastSelectionText = dv;
39697         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39698         this.value = vv;
39699         
39700         
39701     },
39702     // private
39703     reset : function(){
39704         // overridden so that last data is reset..
39705         this.setValue(this.resetValue);
39706         this.clearInvalid();
39707         this.lastData = false;
39708         if (this.view) {
39709             this.view.clearSelections();
39710         }
39711     },
39712     // private
39713     findRecord : function(prop, value){
39714         var record;
39715         if(this.store.getCount() > 0){
39716             this.store.each(function(r){
39717                 if(r.data[prop] == value){
39718                     record = r;
39719                     return false;
39720                 }
39721                 return true;
39722             });
39723         }
39724         return record;
39725     },
39726     
39727     getName: function()
39728     {
39729         // returns hidden if it's set..
39730         if (!this.rendered) {return ''};
39731         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39732         
39733     },
39734     // private
39735     onViewMove : function(e, t){
39736         this.inKeyMode = false;
39737     },
39738
39739     // private
39740     onViewOver : function(e, t){
39741         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39742             return;
39743         }
39744         var item = this.view.findItemFromChild(t);
39745         if(item){
39746             var index = this.view.indexOf(item);
39747             this.select(index, false);
39748         }
39749     },
39750
39751     // private
39752     onViewClick : function(doFocus)
39753     {
39754         var index = this.view.getSelectedIndexes()[0];
39755         var r = this.store.getAt(index);
39756         if(r){
39757             this.onSelect(r, index);
39758         }
39759         if(doFocus !== false && !this.blockFocus){
39760             this.el.focus();
39761         }
39762     },
39763
39764     // private
39765     restrictHeight : function(){
39766         this.innerList.dom.style.height = '';
39767         var inner = this.innerList.dom;
39768         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39769         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39770         this.list.beginUpdate();
39771         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39772         this.list.alignTo(this.el, this.listAlign);
39773         this.list.endUpdate();
39774     },
39775
39776     // private
39777     onEmptyResults : function(){
39778         this.collapse();
39779     },
39780
39781     /**
39782      * Returns true if the dropdown list is expanded, else false.
39783      */
39784     isExpanded : function(){
39785         return this.list.isVisible();
39786     },
39787
39788     /**
39789      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39790      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39791      * @param {String} value The data value of the item to select
39792      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39793      * selected item if it is not currently in view (defaults to true)
39794      * @return {Boolean} True if the value matched an item in the list, else false
39795      */
39796     selectByValue : function(v, scrollIntoView){
39797         if(v !== undefined && v !== null){
39798             var r = this.findRecord(this.valueField || this.displayField, v);
39799             if(r){
39800                 this.select(this.store.indexOf(r), scrollIntoView);
39801                 return true;
39802             }
39803         }
39804         return false;
39805     },
39806
39807     /**
39808      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39809      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39810      * @param {Number} index The zero-based index of the list item to select
39811      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39812      * selected item if it is not currently in view (defaults to true)
39813      */
39814     select : function(index, scrollIntoView){
39815         this.selectedIndex = index;
39816         this.view.select(index);
39817         if(scrollIntoView !== false){
39818             var el = this.view.getNode(index);
39819             if(el){
39820                 this.innerList.scrollChildIntoView(el, false);
39821             }
39822         }
39823     },
39824
39825     // private
39826     selectNext : function(){
39827         var ct = this.store.getCount();
39828         if(ct > 0){
39829             if(this.selectedIndex == -1){
39830                 this.select(0);
39831             }else if(this.selectedIndex < ct-1){
39832                 this.select(this.selectedIndex+1);
39833             }
39834         }
39835     },
39836
39837     // private
39838     selectPrev : function(){
39839         var ct = this.store.getCount();
39840         if(ct > 0){
39841             if(this.selectedIndex == -1){
39842                 this.select(0);
39843             }else if(this.selectedIndex != 0){
39844                 this.select(this.selectedIndex-1);
39845             }
39846         }
39847     },
39848
39849     // private
39850     onKeyUp : function(e){
39851         if(this.editable !== false && !e.isSpecialKey()){
39852             this.lastKey = e.getKey();
39853             this.dqTask.delay(this.queryDelay);
39854         }
39855     },
39856
39857     // private
39858     validateBlur : function(){
39859         return !this.list || !this.list.isVisible();   
39860     },
39861
39862     // private
39863     initQuery : function(){
39864         this.doQuery(this.getRawValue());
39865     },
39866
39867     // private
39868     doForce : function(){
39869         if(this.el.dom.value.length > 0){
39870             this.el.dom.value =
39871                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39872              
39873         }
39874     },
39875
39876     /**
39877      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39878      * query allowing the query action to be canceled if needed.
39879      * @param {String} query The SQL query to execute
39880      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39881      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39882      * saved in the current store (defaults to false)
39883      */
39884     doQuery : function(q, forceAll){
39885         if(q === undefined || q === null){
39886             q = '';
39887         }
39888         var qe = {
39889             query: q,
39890             forceAll: forceAll,
39891             combo: this,
39892             cancel:false
39893         };
39894         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39895             return false;
39896         }
39897         q = qe.query;
39898         forceAll = qe.forceAll;
39899         if(forceAll === true || (q.length >= this.minChars)){
39900             if(this.lastQuery != q || this.alwaysQuery){
39901                 this.lastQuery = q;
39902                 if(this.mode == 'local'){
39903                     this.selectedIndex = -1;
39904                     if(forceAll){
39905                         this.store.clearFilter();
39906                     }else{
39907                         this.store.filter(this.displayField, q);
39908                     }
39909                     this.onLoad();
39910                 }else{
39911                     this.store.baseParams[this.queryParam] = q;
39912                     this.store.load({
39913                         params: this.getParams(q)
39914                     });
39915                     this.expand();
39916                 }
39917             }else{
39918                 this.selectedIndex = -1;
39919                 this.onLoad();   
39920             }
39921         }
39922     },
39923
39924     // private
39925     getParams : function(q){
39926         var p = {};
39927         //p[this.queryParam] = q;
39928         if(this.pageSize){
39929             p.start = 0;
39930             p.limit = this.pageSize;
39931         }
39932         return p;
39933     },
39934
39935     /**
39936      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39937      */
39938     collapse : function(){
39939         if(!this.isExpanded()){
39940             return;
39941         }
39942         this.list.hide();
39943         Roo.get(document).un('mousedown', this.collapseIf, this);
39944         Roo.get(document).un('mousewheel', this.collapseIf, this);
39945         if (!this.editable) {
39946             Roo.get(document).un('keydown', this.listKeyPress, this);
39947         }
39948         this.fireEvent('collapse', this);
39949     },
39950
39951     // private
39952     collapseIf : function(e){
39953         if(!e.within(this.wrap) && !e.within(this.list)){
39954             this.collapse();
39955         }
39956     },
39957
39958     /**
39959      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39960      */
39961     expand : function(){
39962         if(this.isExpanded() || !this.hasFocus){
39963             return;
39964         }
39965         this.list.alignTo(this.el, this.listAlign);
39966         this.list.show();
39967         Roo.get(document).on('mousedown', this.collapseIf, this);
39968         Roo.get(document).on('mousewheel', this.collapseIf, this);
39969         if (!this.editable) {
39970             Roo.get(document).on('keydown', this.listKeyPress, this);
39971         }
39972         
39973         this.fireEvent('expand', this);
39974     },
39975
39976     // private
39977     // Implements the default empty TriggerField.onTriggerClick function
39978     onTriggerClick : function(){
39979         if(this.disabled){
39980             return;
39981         }
39982         if(this.isExpanded()){
39983             this.collapse();
39984             if (!this.blockFocus) {
39985                 this.el.focus();
39986             }
39987             
39988         }else {
39989             this.hasFocus = true;
39990             if(this.triggerAction == 'all') {
39991                 this.doQuery(this.allQuery, true);
39992             } else {
39993                 this.doQuery(this.getRawValue());
39994             }
39995             if (!this.blockFocus) {
39996                 this.el.focus();
39997             }
39998         }
39999     },
40000     listKeyPress : function(e)
40001     {
40002         //Roo.log('listkeypress');
40003         // scroll to first matching element based on key pres..
40004         if (e.isSpecialKey()) {
40005             return false;
40006         }
40007         var k = String.fromCharCode(e.getKey()).toUpperCase();
40008         //Roo.log(k);
40009         var match  = false;
40010         var csel = this.view.getSelectedNodes();
40011         var cselitem = false;
40012         if (csel.length) {
40013             var ix = this.view.indexOf(csel[0]);
40014             cselitem  = this.store.getAt(ix);
40015             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40016                 cselitem = false;
40017             }
40018             
40019         }
40020         
40021         this.store.each(function(v) { 
40022             if (cselitem) {
40023                 // start at existing selection.
40024                 if (cselitem.id == v.id) {
40025                     cselitem = false;
40026                 }
40027                 return;
40028             }
40029                 
40030             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40031                 match = this.store.indexOf(v);
40032                 return false;
40033             }
40034         }, this);
40035         
40036         if (match === false) {
40037             return true; // no more action?
40038         }
40039         // scroll to?
40040         this.view.select(match);
40041         var sn = Roo.get(this.view.getSelectedNodes()[0])
40042         sn.scrollIntoView(sn.dom.parentNode, false);
40043     }
40044
40045     /** 
40046     * @cfg {Boolean} grow 
40047     * @hide 
40048     */
40049     /** 
40050     * @cfg {Number} growMin 
40051     * @hide 
40052     */
40053     /** 
40054     * @cfg {Number} growMax 
40055     * @hide 
40056     */
40057     /**
40058      * @hide
40059      * @method autoSize
40060      */
40061 });/*
40062  * Copyright(c) 2010-2012, Roo J Solutions Limited
40063  *
40064  * Licence LGPL
40065  *
40066  */
40067
40068 /**
40069  * @class Roo.form.ComboBoxArray
40070  * @extends Roo.form.TextField
40071  * A facebook style adder... for lists of email / people / countries  etc...
40072  * pick multiple items from a combo box, and shows each one.
40073  *
40074  *  Fred [x]  Brian [x]  [Pick another |v]
40075  *
40076  *
40077  *  For this to work: it needs various extra information
40078  *    - normal combo problay has
40079  *      name, hiddenName
40080  *    + displayField, valueField
40081  *
40082  *    For our purpose...
40083  *
40084  *
40085  *   If we change from 'extends' to wrapping...
40086  *   
40087  *  
40088  *
40089  
40090  
40091  * @constructor
40092  * Create a new ComboBoxArray.
40093  * @param {Object} config Configuration options
40094  */
40095  
40096
40097 Roo.form.ComboBoxArray = function(config)
40098 {
40099     
40100     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40101     
40102     this.items = new Roo.util.MixedCollection(false);
40103     
40104     // construct the child combo...
40105     
40106     
40107     
40108     
40109    
40110     
40111 }
40112
40113  
40114 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40115
40116     /**
40117      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40118      */
40119     
40120     lastData : false,
40121     
40122     // behavies liek a hiddne field
40123     inputType:      'hidden',
40124     /**
40125      * @cfg {Number} width The width of the box that displays the selected element
40126      */ 
40127     width:          300,
40128
40129     
40130     
40131     /**
40132      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40133      */
40134     name : false,
40135     /**
40136      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40137      */
40138     hiddenName : false,
40139     
40140     
40141     // private the array of items that are displayed..
40142     items  : false,
40143     // private - the hidden field el.
40144     hiddenEl : false,
40145     // private - the filed el..
40146     el : false,
40147     
40148     //validateValue : function() { return true; }, // all values are ok!
40149     //onAddClick: function() { },
40150     
40151     onRender : function(ct, position) 
40152     {
40153         
40154         // create the standard hidden element
40155         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40156         
40157         
40158         // give fake names to child combo;
40159         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40160         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40161         
40162         this.combo = Roo.factory(this.combo, Roo.form);
40163         this.combo.onRender(ct, position);
40164         if (typeof(this.combo.width) != 'undefined') {
40165             this.combo.onResize(this.combo.width,0);
40166         }
40167         
40168         this.combo.initEvents();
40169         
40170         // assigned so form know we need to do this..
40171         this.store          = this.combo.store;
40172         this.valueField     = this.combo.valueField;
40173         this.displayField   = this.combo.displayField ;
40174         
40175         
40176         this.combo.wrap.addClass('x-cbarray-grp');
40177         
40178         var cbwrap = this.combo.wrap.createChild(
40179             {tag: 'div', cls: 'x-cbarray-cb'},
40180             this.combo.el.dom
40181         );
40182         
40183              
40184         this.hiddenEl = this.combo.wrap.createChild({
40185             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40186         });
40187         this.el = this.combo.wrap.createChild({
40188             tag: 'input',  type:'hidden' , name: this.name, value : ''
40189         });
40190          //   this.el.dom.removeAttribute("name");
40191         
40192         
40193         this.outerWrap = this.combo.wrap;
40194         this.wrap = cbwrap;
40195         
40196         this.outerWrap.setWidth(this.width);
40197         this.outerWrap.dom.removeChild(this.el.dom);
40198         
40199         this.wrap.dom.appendChild(this.el.dom);
40200         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40201         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40202         
40203         this.combo.trigger.setStyle('position','relative');
40204         this.combo.trigger.setStyle('left', '0px');
40205         this.combo.trigger.setStyle('top', '2px');
40206         
40207         this.combo.el.setStyle('vertical-align', 'text-bottom');
40208         
40209         //this.trigger.setStyle('vertical-align', 'top');
40210         
40211         // this should use the code from combo really... on('add' ....)
40212         if (this.adder) {
40213             
40214         
40215             this.adder = this.outerWrap.createChild(
40216                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40217             var _t = this;
40218             this.adder.on('click', function(e) {
40219                 _t.fireEvent('adderclick', this, e);
40220             }, _t);
40221         }
40222         //var _t = this;
40223         //this.adder.on('click', this.onAddClick, _t);
40224         
40225         
40226         this.combo.on('select', function(cb, rec, ix) {
40227             this.addItem(rec.data);
40228             
40229             cb.setValue('');
40230             cb.el.dom.value = '';
40231             //cb.lastData = rec.data;
40232             // add to list
40233             
40234         }, this);
40235         
40236         
40237     },
40238     
40239     
40240     getName: function()
40241     {
40242         // returns hidden if it's set..
40243         if (!this.rendered) {return ''};
40244         return  this.hiddenName ? this.hiddenName : this.name;
40245         
40246     },
40247     
40248     
40249     onResize: function(w, h){
40250         
40251         return;
40252         // not sure if this is needed..
40253         //this.combo.onResize(w,h);
40254         
40255         if(typeof w != 'number'){
40256             // we do not handle it!?!?
40257             return;
40258         }
40259         var tw = this.combo.trigger.getWidth();
40260         tw += this.addicon ? this.addicon.getWidth() : 0;
40261         tw += this.editicon ? this.editicon.getWidth() : 0;
40262         var x = w - tw;
40263         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40264             
40265         this.combo.trigger.setStyle('left', '0px');
40266         
40267         if(this.list && this.listWidth === undefined){
40268             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40269             this.list.setWidth(lw);
40270             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40271         }
40272         
40273     
40274         
40275     },
40276     
40277     addItem: function(rec)
40278     {
40279         var valueField = this.combo.valueField;
40280         var displayField = this.combo.displayField;
40281         if (this.items.indexOfKey(rec[valueField]) > -1) {
40282             //console.log("GOT " + rec.data.id);
40283             return;
40284         }
40285         
40286         var x = new Roo.form.ComboBoxArray.Item({
40287             //id : rec[this.idField],
40288             data : rec,
40289             displayField : displayField ,
40290             tipField : displayField ,
40291             cb : this
40292         });
40293         // use the 
40294         this.items.add(rec[valueField],x);
40295         // add it before the element..
40296         this.updateHiddenEl();
40297         x.render(this.outerWrap, this.wrap.dom);
40298         // add the image handler..
40299     },
40300     
40301     updateHiddenEl : function()
40302     {
40303         this.validate();
40304         if (!this.hiddenEl) {
40305             return;
40306         }
40307         var ar = [];
40308         var idField = this.combo.valueField;
40309         
40310         this.items.each(function(f) {
40311             ar.push(f.data[idField]);
40312            
40313         });
40314         this.hiddenEl.dom.value = ar.join(',');
40315         this.validate();
40316     },
40317     
40318     reset : function()
40319     {
40320         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40321         this.items.each(function(f) {
40322            f.remove(); 
40323         });
40324         this.el.dom.value = '';
40325         if (this.hiddenEl) {
40326             this.hiddenEl.dom.value = '';
40327         }
40328         
40329     },
40330     getValue: function()
40331     {
40332         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40333     },
40334     setValue: function(v) // not a valid action - must use addItems..
40335     {
40336          
40337         this.reset();
40338         
40339         
40340         
40341         if (this.store.isLocal && (typeof(v) == 'string')) {
40342             // then we can use the store to find the values..
40343             // comma seperated at present.. this needs to allow JSON based encoding..
40344             this.hiddenEl.value  = v;
40345             var v_ar = [];
40346             Roo.each(v.split(','), function(k) {
40347                 Roo.log("CHECK " + this.valueField + ',' + k);
40348                 var li = this.store.query(this.valueField, k);
40349                 if (!li.length) {
40350                     return;
40351                 }
40352                 var add = {};
40353                 add[this.valueField] = k;
40354                 add[this.displayField] = li.item(0).data[this.displayField];
40355                 
40356                 this.addItem(add);
40357             }, this) 
40358              
40359         }
40360         if (typeof(v) == 'object') {
40361             // then let's assume it's an array of objects..
40362             Roo.each(v, function(l) {
40363                 this.addItem(l);
40364             }, this);
40365              
40366         }
40367         
40368         
40369     },
40370     setFromData: function(v)
40371     {
40372         // this recieves an object, if setValues is called.
40373         this.reset();
40374         this.el.dom.value = v[this.displayField];
40375         this.hiddenEl.dom.value = v[this.valueField];
40376         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40377             return;
40378         }
40379         var kv = v[this.valueField];
40380         var dv = v[this.displayField];
40381         kv = typeof(kv) != 'string' ? '' : kv;
40382         dv = typeof(dv) != 'string' ? '' : dv;
40383         
40384         
40385         var keys = kv.split(',');
40386         var display = dv.split(',');
40387         for (var i = 0 ; i < keys.length; i++) {
40388             
40389             add = {};
40390             add[this.valueField] = keys[i];
40391             add[this.displayField] = display[i];
40392             this.addItem(add);
40393         }
40394       
40395         
40396     },
40397     
40398     /**
40399      * Validates the combox array value
40400      * @return {Boolean} True if the value is valid, else false
40401      */
40402     validate : function(){
40403         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40404             this.clearInvalid();
40405             return true;
40406         }
40407         return false;
40408     },
40409     
40410     validateValue : function(value){
40411         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40412         
40413     }
40414     
40415 });
40416
40417
40418
40419 /**
40420  * @class Roo.form.ComboBoxArray.Item
40421  * @extends Roo.BoxComponent
40422  * A selected item in the list
40423  *  Fred [x]  Brian [x]  [Pick another |v]
40424  * 
40425  * @constructor
40426  * Create a new item.
40427  * @param {Object} config Configuration options
40428  */
40429  
40430 Roo.form.ComboBoxArray.Item = function(config) {
40431     config.id = Roo.id();
40432     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40433 }
40434
40435 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40436     data : {},
40437     cb: false,
40438     displayField : false,
40439     tipField : false,
40440     
40441     
40442     defaultAutoCreate : {
40443         tag: 'div',
40444         cls: 'x-cbarray-item',
40445         cn : [ 
40446             { tag: 'div' },
40447             {
40448                 tag: 'img',
40449                 width:16,
40450                 height : 16,
40451                 src : Roo.BLANK_IMAGE_URL ,
40452                 align: 'center'
40453             }
40454         ]
40455         
40456     },
40457     
40458  
40459     onRender : function(ct, position)
40460     {
40461         Roo.form.Field.superclass.onRender.call(this, ct, position);
40462         
40463         if(!this.el){
40464             var cfg = this.getAutoCreate();
40465             this.el = ct.createChild(cfg, position);
40466         }
40467         
40468         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40469         
40470         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40471             this.cb.renderer(this.data) :
40472             String.format('{0}',this.data[this.displayField]);
40473         
40474             
40475         this.el.child('div').dom.setAttribute('qtip',
40476                         String.format('{0}',this.data[this.tipField])
40477         );
40478         
40479         this.el.child('img').on('click', this.remove, this);
40480         
40481     },
40482    
40483     remove : function()
40484     {
40485         
40486         this.cb.items.remove(this);
40487         this.el.child('img').un('click', this.remove, this);
40488         this.el.remove();
40489         this.cb.updateHiddenEl();
40490     }
40491     
40492 });/*
40493  * Based on:
40494  * Ext JS Library 1.1.1
40495  * Copyright(c) 2006-2007, Ext JS, LLC.
40496  *
40497  * Originally Released Under LGPL - original licence link has changed is not relivant.
40498  *
40499  * Fork - LGPL
40500  * <script type="text/javascript">
40501  */
40502 /**
40503  * @class Roo.form.Checkbox
40504  * @extends Roo.form.Field
40505  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40506  * @constructor
40507  * Creates a new Checkbox
40508  * @param {Object} config Configuration options
40509  */
40510 Roo.form.Checkbox = function(config){
40511     Roo.form.Checkbox.superclass.constructor.call(this, config);
40512     this.addEvents({
40513         /**
40514          * @event check
40515          * Fires when the checkbox is checked or unchecked.
40516              * @param {Roo.form.Checkbox} this This checkbox
40517              * @param {Boolean} checked The new checked value
40518              */
40519         check : true
40520     });
40521 };
40522
40523 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40524     /**
40525      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40526      */
40527     focusClass : undefined,
40528     /**
40529      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40530      */
40531     fieldClass: "x-form-field",
40532     /**
40533      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40534      */
40535     checked: false,
40536     /**
40537      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40538      * {tag: "input", type: "checkbox", autocomplete: "off"})
40539      */
40540     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40541     /**
40542      * @cfg {String} boxLabel The text that appears beside the checkbox
40543      */
40544     boxLabel : "",
40545     /**
40546      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40547      */  
40548     inputValue : '1',
40549     /**
40550      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40551      */
40552      valueOff: '0', // value when not checked..
40553
40554     actionMode : 'viewEl', 
40555     //
40556     // private
40557     itemCls : 'x-menu-check-item x-form-item',
40558     groupClass : 'x-menu-group-item',
40559     inputType : 'hidden',
40560     
40561     
40562     inSetChecked: false, // check that we are not calling self...
40563     
40564     inputElement: false, // real input element?
40565     basedOn: false, // ????
40566     
40567     isFormField: true, // not sure where this is needed!!!!
40568
40569     onResize : function(){
40570         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40571         if(!this.boxLabel){
40572             this.el.alignTo(this.wrap, 'c-c');
40573         }
40574     },
40575
40576     initEvents : function(){
40577         Roo.form.Checkbox.superclass.initEvents.call(this);
40578         this.el.on("click", this.onClick,  this);
40579         this.el.on("change", this.onClick,  this);
40580     },
40581
40582
40583     getResizeEl : function(){
40584         return this.wrap;
40585     },
40586
40587     getPositionEl : function(){
40588         return this.wrap;
40589     },
40590
40591     // private
40592     onRender : function(ct, position){
40593         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40594         /*
40595         if(this.inputValue !== undefined){
40596             this.el.dom.value = this.inputValue;
40597         }
40598         */
40599         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40600         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40601         var viewEl = this.wrap.createChild({ 
40602             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40603         this.viewEl = viewEl;   
40604         this.wrap.on('click', this.onClick,  this); 
40605         
40606         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40607         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40608         
40609         
40610         
40611         if(this.boxLabel){
40612             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40613         //    viewEl.on('click', this.onClick,  this); 
40614         }
40615         //if(this.checked){
40616             this.setChecked(this.checked);
40617         //}else{
40618             //this.checked = this.el.dom;
40619         //}
40620
40621     },
40622
40623     // private
40624     initValue : Roo.emptyFn,
40625
40626     /**
40627      * Returns the checked state of the checkbox.
40628      * @return {Boolean} True if checked, else false
40629      */
40630     getValue : function(){
40631         if(this.el){
40632             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40633         }
40634         return this.valueOff;
40635         
40636     },
40637
40638         // private
40639     onClick : function(){ 
40640         this.setChecked(!this.checked);
40641
40642         //if(this.el.dom.checked != this.checked){
40643         //    this.setValue(this.el.dom.checked);
40644        // }
40645     },
40646
40647     /**
40648      * Sets the checked state of the checkbox.
40649      * On is always based on a string comparison between inputValue and the param.
40650      * @param {Boolean/String} value - the value to set 
40651      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40652      */
40653     setValue : function(v,suppressEvent){
40654         
40655         
40656         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40657         //if(this.el && this.el.dom){
40658         //    this.el.dom.checked = this.checked;
40659         //    this.el.dom.defaultChecked = this.checked;
40660         //}
40661         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40662         //this.fireEvent("check", this, this.checked);
40663     },
40664     // private..
40665     setChecked : function(state,suppressEvent)
40666     {
40667         if (this.inSetChecked) {
40668             this.checked = state;
40669             return;
40670         }
40671         
40672     
40673         if(this.wrap){
40674             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40675         }
40676         this.checked = state;
40677         if(suppressEvent !== true){
40678             this.fireEvent('check', this, state);
40679         }
40680         this.inSetChecked = true;
40681         this.el.dom.value = state ? this.inputValue : this.valueOff;
40682         this.inSetChecked = false;
40683         
40684     },
40685     // handle setting of hidden value by some other method!!?!?
40686     setFromHidden: function()
40687     {
40688         if(!this.el){
40689             return;
40690         }
40691         //console.log("SET FROM HIDDEN");
40692         //alert('setFrom hidden');
40693         this.setValue(this.el.dom.value);
40694     },
40695     
40696     onDestroy : function()
40697     {
40698         if(this.viewEl){
40699             Roo.get(this.viewEl).remove();
40700         }
40701          
40702         Roo.form.Checkbox.superclass.onDestroy.call(this);
40703     }
40704
40705 });/*
40706  * Based on:
40707  * Ext JS Library 1.1.1
40708  * Copyright(c) 2006-2007, Ext JS, LLC.
40709  *
40710  * Originally Released Under LGPL - original licence link has changed is not relivant.
40711  *
40712  * Fork - LGPL
40713  * <script type="text/javascript">
40714  */
40715  
40716 /**
40717  * @class Roo.form.Radio
40718  * @extends Roo.form.Checkbox
40719  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40720  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40721  * @constructor
40722  * Creates a new Radio
40723  * @param {Object} config Configuration options
40724  */
40725 Roo.form.Radio = function(){
40726     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40727 };
40728 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40729     inputType: 'radio',
40730
40731     /**
40732      * If this radio is part of a group, it will return the selected value
40733      * @return {String}
40734      */
40735     getGroupValue : function(){
40736         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40737     },
40738     
40739     
40740     onRender : function(ct, position){
40741         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40742         
40743         if(this.inputValue !== undefined){
40744             this.el.dom.value = this.inputValue;
40745         }
40746          
40747         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40748         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40749         //var viewEl = this.wrap.createChild({ 
40750         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40751         //this.viewEl = viewEl;   
40752         //this.wrap.on('click', this.onClick,  this); 
40753         
40754         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40755         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40756         
40757         
40758         
40759         if(this.boxLabel){
40760             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40761         //    viewEl.on('click', this.onClick,  this); 
40762         }
40763          if(this.checked){
40764             this.el.dom.checked =   'checked' ;
40765         }
40766          
40767     } 
40768     
40769     
40770 });//<script type="text/javascript">
40771
40772 /*
40773  * Ext JS Library 1.1.1
40774  * Copyright(c) 2006-2007, Ext JS, LLC.
40775  * licensing@extjs.com
40776  * 
40777  * http://www.extjs.com/license
40778  */
40779  
40780  /*
40781   * 
40782   * Known bugs:
40783   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40784   * - IE ? - no idea how much works there.
40785   * 
40786   * 
40787   * 
40788   */
40789  
40790
40791 /**
40792  * @class Ext.form.HtmlEditor
40793  * @extends Ext.form.Field
40794  * Provides a lightweight HTML Editor component.
40795  *
40796  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40797  * 
40798  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40799  * supported by this editor.</b><br/><br/>
40800  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40801  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40802  */
40803 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40804       /**
40805      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40806      */
40807     toolbars : false,
40808     /**
40809      * @cfg {String} createLinkText The default text for the create link prompt
40810      */
40811     createLinkText : 'Please enter the URL for the link:',
40812     /**
40813      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40814      */
40815     defaultLinkValue : 'http:/'+'/',
40816    
40817      /**
40818      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40819      *                        Roo.resizable.
40820      */
40821     resizable : false,
40822      /**
40823      * @cfg {Number} height (in pixels)
40824      */   
40825     height: 300,
40826    /**
40827      * @cfg {Number} width (in pixels)
40828      */   
40829     width: 500,
40830     
40831     /**
40832      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40833      * 
40834      */
40835     stylesheets: false,
40836     
40837     // id of frame..
40838     frameId: false,
40839     
40840     // private properties
40841     validationEvent : false,
40842     deferHeight: true,
40843     initialized : false,
40844     activated : false,
40845     sourceEditMode : false,
40846     onFocus : Roo.emptyFn,
40847     iframePad:3,
40848     hideMode:'offsets',
40849     
40850     defaultAutoCreate : { // modified by initCompnoent..
40851         tag: "textarea",
40852         style:"width:500px;height:300px;",
40853         autocomplete: "off"
40854     },
40855
40856     // private
40857     initComponent : function(){
40858         this.addEvents({
40859             /**
40860              * @event initialize
40861              * Fires when the editor is fully initialized (including the iframe)
40862              * @param {HtmlEditor} this
40863              */
40864             initialize: true,
40865             /**
40866              * @event activate
40867              * Fires when the editor is first receives the focus. Any insertion must wait
40868              * until after this event.
40869              * @param {HtmlEditor} this
40870              */
40871             activate: true,
40872              /**
40873              * @event beforesync
40874              * Fires before the textarea is updated with content from the editor iframe. Return false
40875              * to cancel the sync.
40876              * @param {HtmlEditor} this
40877              * @param {String} html
40878              */
40879             beforesync: true,
40880              /**
40881              * @event beforepush
40882              * Fires before the iframe editor is updated with content from the textarea. Return false
40883              * to cancel the push.
40884              * @param {HtmlEditor} this
40885              * @param {String} html
40886              */
40887             beforepush: true,
40888              /**
40889              * @event sync
40890              * Fires when the textarea is updated with content from the editor iframe.
40891              * @param {HtmlEditor} this
40892              * @param {String} html
40893              */
40894             sync: true,
40895              /**
40896              * @event push
40897              * Fires when the iframe editor is updated with content from the textarea.
40898              * @param {HtmlEditor} this
40899              * @param {String} html
40900              */
40901             push: true,
40902              /**
40903              * @event editmodechange
40904              * Fires when the editor switches edit modes
40905              * @param {HtmlEditor} this
40906              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40907              */
40908             editmodechange: true,
40909             /**
40910              * @event editorevent
40911              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40912              * @param {HtmlEditor} this
40913              */
40914             editorevent: true
40915         });
40916         this.defaultAutoCreate =  {
40917             tag: "textarea",
40918             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40919             autocomplete: "off"
40920         };
40921     },
40922
40923     /**
40924      * Protected method that will not generally be called directly. It
40925      * is called when the editor creates its toolbar. Override this method if you need to
40926      * add custom toolbar buttons.
40927      * @param {HtmlEditor} editor
40928      */
40929     createToolbar : function(editor){
40930         if (!editor.toolbars || !editor.toolbars.length) {
40931             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40932         }
40933         
40934         for (var i =0 ; i < editor.toolbars.length;i++) {
40935             editor.toolbars[i] = Roo.factory(
40936                     typeof(editor.toolbars[i]) == 'string' ?
40937                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40938                 Roo.form.HtmlEditor);
40939             editor.toolbars[i].init(editor);
40940         }
40941          
40942         
40943     },
40944
40945     /**
40946      * Protected method that will not generally be called directly. It
40947      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40948      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40949      */
40950     getDocMarkup : function(){
40951         // body styles..
40952         var st = '';
40953         if (this.stylesheets === false) {
40954             
40955             Roo.get(document.head).select('style').each(function(node) {
40956                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40957             });
40958             
40959             Roo.get(document.head).select('link').each(function(node) { 
40960                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40961             });
40962             
40963         } else if (!this.stylesheets.length) {
40964                 // simple..
40965                 st = '<style type="text/css">' +
40966                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40967                    '</style>';
40968         } else {
40969             Roo.each(this.stylesheets, function(s) {
40970                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40971             });
40972             
40973         }
40974         
40975         st +=  '<style type="text/css">' +
40976             'IMG { cursor: pointer } ' +
40977         '</style>';
40978
40979         
40980         return '<html><head>' + st  +
40981             //<style type="text/css">' +
40982             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40983             //'</style>' +
40984             ' </head><body class="roo-htmleditor-body"></body></html>';
40985     },
40986
40987     // private
40988     onRender : function(ct, position)
40989     {
40990         var _t = this;
40991         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40992         this.el.dom.style.border = '0 none';
40993         this.el.dom.setAttribute('tabIndex', -1);
40994         this.el.addClass('x-hidden');
40995         if(Roo.isIE){ // fix IE 1px bogus margin
40996             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40997         }
40998         this.wrap = this.el.wrap({
40999             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
41000         });
41001         
41002         if (this.resizable) {
41003             this.resizeEl = new Roo.Resizable(this.wrap, {
41004                 pinned : true,
41005                 wrap: true,
41006                 dynamic : true,
41007                 minHeight : this.height,
41008                 height: this.height,
41009                 handles : this.resizable,
41010                 width: this.width,
41011                 listeners : {
41012                     resize : function(r, w, h) {
41013                         _t.onResize(w,h); // -something
41014                     }
41015                 }
41016             });
41017             
41018         }
41019
41020         this.frameId = Roo.id();
41021         
41022         this.createToolbar(this);
41023         
41024       
41025         
41026         var iframe = this.wrap.createChild({
41027             tag: 'iframe',
41028             id: this.frameId,
41029             name: this.frameId,
41030             frameBorder : 'no',
41031             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41032         }, this.el
41033         );
41034         
41035        // console.log(iframe);
41036         //this.wrap.dom.appendChild(iframe);
41037
41038         this.iframe = iframe.dom;
41039
41040          this.assignDocWin();
41041         
41042         this.doc.designMode = 'on';
41043        
41044         this.doc.open();
41045         this.doc.write(this.getDocMarkup());
41046         this.doc.close();
41047
41048         
41049         var task = { // must defer to wait for browser to be ready
41050             run : function(){
41051                 //console.log("run task?" + this.doc.readyState);
41052                 this.assignDocWin();
41053                 if(this.doc.body || this.doc.readyState == 'complete'){
41054                     try {
41055                         this.doc.designMode="on";
41056                     } catch (e) {
41057                         return;
41058                     }
41059                     Roo.TaskMgr.stop(task);
41060                     this.initEditor.defer(10, this);
41061                 }
41062             },
41063             interval : 10,
41064             duration:10000,
41065             scope: this
41066         };
41067         Roo.TaskMgr.start(task);
41068
41069         if(!this.width){
41070             this.setSize(this.wrap.getSize());
41071         }
41072         if (this.resizeEl) {
41073             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
41074             // should trigger onReize..
41075         }
41076     },
41077
41078     // private
41079     onResize : function(w, h)
41080     {
41081         //Roo.log('resize: ' +w + ',' + h );
41082         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
41083         if(this.el && this.iframe){
41084             if(typeof w == 'number'){
41085                 var aw = w - this.wrap.getFrameWidth('lr');
41086                 this.el.setWidth(this.adjustWidth('textarea', aw));
41087                 this.iframe.style.width = aw + 'px';
41088             }
41089             if(typeof h == 'number'){
41090                 var tbh = 0;
41091                 for (var i =0; i < this.toolbars.length;i++) {
41092                     // fixme - ask toolbars for heights?
41093                     tbh += this.toolbars[i].tb.el.getHeight();
41094                     if (this.toolbars[i].footer) {
41095                         tbh += this.toolbars[i].footer.el.getHeight();
41096                     }
41097                 }
41098                 
41099                 
41100                 
41101                 
41102                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
41103                 ah -= 5; // knock a few pixes off for look..
41104                 this.el.setHeight(this.adjustWidth('textarea', ah));
41105                 this.iframe.style.height = ah + 'px';
41106                 if(this.doc){
41107                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
41108                 }
41109             }
41110         }
41111     },
41112
41113     /**
41114      * Toggles the editor between standard and source edit mode.
41115      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41116      */
41117     toggleSourceEdit : function(sourceEditMode){
41118         
41119         this.sourceEditMode = sourceEditMode === true;
41120         
41121         if(this.sourceEditMode){
41122 //            Roo.log('in');
41123 //            Roo.log(this.syncValue());
41124             this.syncValue();
41125             this.iframe.className = 'x-hidden';
41126             this.el.removeClass('x-hidden');
41127             this.el.dom.removeAttribute('tabIndex');
41128             this.el.focus();
41129         }else{
41130 //            Roo.log('out')
41131 //            Roo.log(this.pushValue()); 
41132             this.pushValue();
41133             this.iframe.className = '';
41134             this.el.addClass('x-hidden');
41135             this.el.dom.setAttribute('tabIndex', -1);
41136             this.deferFocus();
41137         }
41138         this.setSize(this.wrap.getSize());
41139         this.fireEvent('editmodechange', this, this.sourceEditMode);
41140     },
41141
41142     // private used internally
41143     createLink : function(){
41144         var url = prompt(this.createLinkText, this.defaultLinkValue);
41145         if(url && url != 'http:/'+'/'){
41146             this.relayCmd('createlink', url);
41147         }
41148     },
41149
41150     // private (for BoxComponent)
41151     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41152
41153     // private (for BoxComponent)
41154     getResizeEl : function(){
41155         return this.wrap;
41156     },
41157
41158     // private (for BoxComponent)
41159     getPositionEl : function(){
41160         return this.wrap;
41161     },
41162
41163     // private
41164     initEvents : function(){
41165         this.originalValue = this.getValue();
41166     },
41167
41168     /**
41169      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41170      * @method
41171      */
41172     markInvalid : Roo.emptyFn,
41173     /**
41174      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41175      * @method
41176      */
41177     clearInvalid : Roo.emptyFn,
41178
41179     setValue : function(v){
41180         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
41181         this.pushValue();
41182     },
41183
41184     /**
41185      * Protected method that will not generally be called directly. If you need/want
41186      * custom HTML cleanup, this is the method you should override.
41187      * @param {String} html The HTML to be cleaned
41188      * return {String} The cleaned HTML
41189      */
41190     cleanHtml : function(html){
41191         html = String(html);
41192         if(html.length > 5){
41193             if(Roo.isSafari){ // strip safari nonsense
41194                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41195             }
41196         }
41197         if(html == '&nbsp;'){
41198             html = '';
41199         }
41200         return html;
41201     },
41202
41203     /**
41204      * Protected method that will not generally be called directly. Syncs the contents
41205      * of the editor iframe with the textarea.
41206      */
41207     syncValue : function(){
41208         if(this.initialized){
41209             var bd = (this.doc.body || this.doc.documentElement);
41210             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41211             var html = bd.innerHTML;
41212             if(Roo.isSafari){
41213                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41214                 var m = bs.match(/text-align:(.*?);/i);
41215                 if(m && m[1]){
41216                     html = '<div style="'+m[0]+'">' + html + '</div>';
41217                 }
41218             }
41219             html = this.cleanHtml(html);
41220             // fix up the special chars.. normaly like back quotes in word...
41221             // however we do not want to do this with chinese..
41222             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41223                 var cc = b.charCodeAt();
41224                 if (
41225                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41226                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41227                     (cc >= 0xf900 && cc < 0xfb00 )
41228                 ) {
41229                         return b;
41230                 }
41231                 return "&#"+cc+";" 
41232             });
41233             if(this.fireEvent('beforesync', this, html) !== false){
41234                 this.el.dom.value = html;
41235                 this.fireEvent('sync', this, html);
41236             }
41237         }
41238     },
41239
41240     /**
41241      * Protected method that will not generally be called directly. Pushes the value of the textarea
41242      * into the iframe editor.
41243      */
41244     pushValue : function(){
41245         if(this.initialized){
41246             var v = this.el.dom.value;
41247             
41248             if(v.length < 1){
41249                 v = '&#160;';
41250             }
41251             
41252             if(this.fireEvent('beforepush', this, v) !== false){
41253                 var d = (this.doc.body || this.doc.documentElement);
41254                 d.innerHTML = v;
41255                 this.cleanUpPaste();
41256                 this.el.dom.value = d.innerHTML;
41257                 this.fireEvent('push', this, v);
41258             }
41259         }
41260     },
41261
41262     // private
41263     deferFocus : function(){
41264         this.focus.defer(10, this);
41265     },
41266
41267     // doc'ed in Field
41268     focus : function(){
41269         if(this.win && !this.sourceEditMode){
41270             this.win.focus();
41271         }else{
41272             this.el.focus();
41273         }
41274     },
41275     
41276     assignDocWin: function()
41277     {
41278         var iframe = this.iframe;
41279         
41280          if(Roo.isIE){
41281             this.doc = iframe.contentWindow.document;
41282             this.win = iframe.contentWindow;
41283         } else {
41284             if (!Roo.get(this.frameId)) {
41285                 return;
41286             }
41287             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41288             this.win = Roo.get(this.frameId).dom.contentWindow;
41289         }
41290     },
41291     
41292     // private
41293     initEditor : function(){
41294         //console.log("INIT EDITOR");
41295         this.assignDocWin();
41296         
41297         
41298         
41299         this.doc.designMode="on";
41300         this.doc.open();
41301         this.doc.write(this.getDocMarkup());
41302         this.doc.close();
41303         
41304         var dbody = (this.doc.body || this.doc.documentElement);
41305         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41306         // this copies styles from the containing element into thsi one..
41307         // not sure why we need all of this..
41308         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41309         ss['background-attachment'] = 'fixed'; // w3c
41310         dbody.bgProperties = 'fixed'; // ie
41311         Roo.DomHelper.applyStyles(dbody, ss);
41312         Roo.EventManager.on(this.doc, {
41313             //'mousedown': this.onEditorEvent,
41314             'mouseup': this.onEditorEvent,
41315             'dblclick': this.onEditorEvent,
41316             'click': this.onEditorEvent,
41317             'keyup': this.onEditorEvent,
41318             buffer:100,
41319             scope: this
41320         });
41321         if(Roo.isGecko){
41322             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41323         }
41324         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41325             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41326         }
41327         this.initialized = true;
41328
41329         this.fireEvent('initialize', this);
41330         this.pushValue();
41331     },
41332
41333     // private
41334     onDestroy : function(){
41335         
41336         
41337         
41338         if(this.rendered){
41339             
41340             for (var i =0; i < this.toolbars.length;i++) {
41341                 // fixme - ask toolbars for heights?
41342                 this.toolbars[i].onDestroy();
41343             }
41344             
41345             this.wrap.dom.innerHTML = '';
41346             this.wrap.remove();
41347         }
41348     },
41349
41350     // private
41351     onFirstFocus : function(){
41352         
41353         this.assignDocWin();
41354         
41355         
41356         this.activated = true;
41357         for (var i =0; i < this.toolbars.length;i++) {
41358             this.toolbars[i].onFirstFocus();
41359         }
41360        
41361         if(Roo.isGecko){ // prevent silly gecko errors
41362             this.win.focus();
41363             var s = this.win.getSelection();
41364             if(!s.focusNode || s.focusNode.nodeType != 3){
41365                 var r = s.getRangeAt(0);
41366                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41367                 r.collapse(true);
41368                 this.deferFocus();
41369             }
41370             try{
41371                 this.execCmd('useCSS', true);
41372                 this.execCmd('styleWithCSS', false);
41373             }catch(e){}
41374         }
41375         this.fireEvent('activate', this);
41376     },
41377
41378     // private
41379     adjustFont: function(btn){
41380         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41381         //if(Roo.isSafari){ // safari
41382         //    adjust *= 2;
41383        // }
41384         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41385         if(Roo.isSafari){ // safari
41386             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41387             v =  (v < 10) ? 10 : v;
41388             v =  (v > 48) ? 48 : v;
41389             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41390             
41391         }
41392         
41393         
41394         v = Math.max(1, v+adjust);
41395         
41396         this.execCmd('FontSize', v  );
41397     },
41398
41399     onEditorEvent : function(e){
41400         this.fireEvent('editorevent', this, e);
41401       //  this.updateToolbar();
41402         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41403     },
41404
41405     insertTag : function(tg)
41406     {
41407         // could be a bit smarter... -> wrap the current selected tRoo..
41408         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41409             
41410             range = this.createRange(this.getSelection());
41411             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41412             wrappingNode.appendChild(range.extractContents());
41413             range.insertNode(wrappingNode);
41414
41415             return;
41416             
41417             
41418             
41419         }
41420         this.execCmd("formatblock",   tg);
41421         
41422     },
41423     
41424     insertText : function(txt)
41425     {
41426         
41427         
41428         var range = this.createRange();
41429         range.deleteContents();
41430                //alert(Sender.getAttribute('label'));
41431                
41432         range.insertNode(this.doc.createTextNode(txt));
41433     } ,
41434     
41435     // private
41436     relayBtnCmd : function(btn){
41437         this.relayCmd(btn.cmd);
41438     },
41439
41440     /**
41441      * Executes a Midas editor command on the editor document and performs necessary focus and
41442      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41443      * @param {String} cmd The Midas command
41444      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41445      */
41446     relayCmd : function(cmd, value){
41447         this.win.focus();
41448         this.execCmd(cmd, value);
41449         this.fireEvent('editorevent', this);
41450         //this.updateToolbar();
41451         this.deferFocus();
41452     },
41453
41454     /**
41455      * Executes a Midas editor command directly on the editor document.
41456      * For visual commands, you should use {@link #relayCmd} instead.
41457      * <b>This should only be called after the editor is initialized.</b>
41458      * @param {String} cmd The Midas command
41459      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41460      */
41461     execCmd : function(cmd, value){
41462         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41463         this.syncValue();
41464     },
41465  
41466  
41467    
41468     /**
41469      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41470      * to insert tRoo.
41471      * @param {String} text | dom node.. 
41472      */
41473     insertAtCursor : function(text)
41474     {
41475         
41476         
41477         
41478         if(!this.activated){
41479             return;
41480         }
41481         /*
41482         if(Roo.isIE){
41483             this.win.focus();
41484             var r = this.doc.selection.createRange();
41485             if(r){
41486                 r.collapse(true);
41487                 r.pasteHTML(text);
41488                 this.syncValue();
41489                 this.deferFocus();
41490             
41491             }
41492             return;
41493         }
41494         */
41495         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41496             this.win.focus();
41497             
41498             
41499             // from jquery ui (MIT licenced)
41500             var range, node;
41501             var win = this.win;
41502             
41503             if (win.getSelection && win.getSelection().getRangeAt) {
41504                 range = win.getSelection().getRangeAt(0);
41505                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41506                 range.insertNode(node);
41507             } else if (win.document.selection && win.document.selection.createRange) {
41508                 // no firefox support
41509                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41510                 win.document.selection.createRange().pasteHTML(txt);
41511             } else {
41512                 // no firefox support
41513                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41514                 this.execCmd('InsertHTML', txt);
41515             } 
41516             
41517             this.syncValue();
41518             
41519             this.deferFocus();
41520         }
41521     },
41522  // private
41523     mozKeyPress : function(e){
41524         if(e.ctrlKey){
41525             var c = e.getCharCode(), cmd;
41526           
41527             if(c > 0){
41528                 c = String.fromCharCode(c).toLowerCase();
41529                 switch(c){
41530                     case 'b':
41531                         cmd = 'bold';
41532                         break;
41533                     case 'i':
41534                         cmd = 'italic';
41535                         break;
41536                     
41537                     case 'u':
41538                         cmd = 'underline';
41539                         break;
41540                     
41541                     case 'v':
41542                         this.cleanUpPaste.defer(100, this);
41543                         return;
41544                         
41545                 }
41546                 if(cmd){
41547                     this.win.focus();
41548                     this.execCmd(cmd);
41549                     this.deferFocus();
41550                     e.preventDefault();
41551                 }
41552                 
41553             }
41554         }
41555     },
41556
41557     // private
41558     fixKeys : function(){ // load time branching for fastest keydown performance
41559         if(Roo.isIE){
41560             return function(e){
41561                 var k = e.getKey(), r;
41562                 if(k == e.TAB){
41563                     e.stopEvent();
41564                     r = this.doc.selection.createRange();
41565                     if(r){
41566                         r.collapse(true);
41567                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41568                         this.deferFocus();
41569                     }
41570                     return;
41571                 }
41572                 
41573                 if(k == e.ENTER){
41574                     r = this.doc.selection.createRange();
41575                     if(r){
41576                         var target = r.parentElement();
41577                         if(!target || target.tagName.toLowerCase() != 'li'){
41578                             e.stopEvent();
41579                             r.pasteHTML('<br />');
41580                             r.collapse(false);
41581                             r.select();
41582                         }
41583                     }
41584                 }
41585                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41586                     this.cleanUpPaste.defer(100, this);
41587                     return;
41588                 }
41589                 
41590                 
41591             };
41592         }else if(Roo.isOpera){
41593             return function(e){
41594                 var k = e.getKey();
41595                 if(k == e.TAB){
41596                     e.stopEvent();
41597                     this.win.focus();
41598                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41599                     this.deferFocus();
41600                 }
41601                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41602                     this.cleanUpPaste.defer(100, this);
41603                     return;
41604                 }
41605                 
41606             };
41607         }else if(Roo.isSafari){
41608             return function(e){
41609                 var k = e.getKey();
41610                 
41611                 if(k == e.TAB){
41612                     e.stopEvent();
41613                     this.execCmd('InsertText','\t');
41614                     this.deferFocus();
41615                     return;
41616                 }
41617                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41618                     this.cleanUpPaste.defer(100, this);
41619                     return;
41620                 }
41621                 
41622              };
41623         }
41624     }(),
41625     
41626     getAllAncestors: function()
41627     {
41628         var p = this.getSelectedNode();
41629         var a = [];
41630         if (!p) {
41631             a.push(p); // push blank onto stack..
41632             p = this.getParentElement();
41633         }
41634         
41635         
41636         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41637             a.push(p);
41638             p = p.parentNode;
41639         }
41640         a.push(this.doc.body);
41641         return a;
41642     },
41643     lastSel : false,
41644     lastSelNode : false,
41645     
41646     
41647     getSelection : function() 
41648     {
41649         this.assignDocWin();
41650         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41651     },
41652     
41653     getSelectedNode: function() 
41654     {
41655         // this may only work on Gecko!!!
41656         
41657         // should we cache this!!!!
41658         
41659         
41660         
41661          
41662         var range = this.createRange(this.getSelection()).cloneRange();
41663         
41664         if (Roo.isIE) {
41665             var parent = range.parentElement();
41666             while (true) {
41667                 var testRange = range.duplicate();
41668                 testRange.moveToElementText(parent);
41669                 if (testRange.inRange(range)) {
41670                     break;
41671                 }
41672                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41673                     break;
41674                 }
41675                 parent = parent.parentElement;
41676             }
41677             return parent;
41678         }
41679         
41680         // is ancestor a text element.
41681         var ac =  range.commonAncestorContainer;
41682         if (ac.nodeType == 3) {
41683             ac = ac.parentNode;
41684         }
41685         
41686         var ar = ac.childNodes;
41687          
41688         var nodes = [];
41689         var other_nodes = [];
41690         var has_other_nodes = false;
41691         for (var i=0;i<ar.length;i++) {
41692             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41693                 continue;
41694             }
41695             // fullly contained node.
41696             
41697             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41698                 nodes.push(ar[i]);
41699                 continue;
41700             }
41701             
41702             // probably selected..
41703             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41704                 other_nodes.push(ar[i]);
41705                 continue;
41706             }
41707             // outer..
41708             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41709                 continue;
41710             }
41711             
41712             
41713             has_other_nodes = true;
41714         }
41715         if (!nodes.length && other_nodes.length) {
41716             nodes= other_nodes;
41717         }
41718         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41719             return false;
41720         }
41721         
41722         return nodes[0];
41723     },
41724     createRange: function(sel)
41725     {
41726         // this has strange effects when using with 
41727         // top toolbar - not sure if it's a great idea.
41728         //this.editor.contentWindow.focus();
41729         if (typeof sel != "undefined") {
41730             try {
41731                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41732             } catch(e) {
41733                 return this.doc.createRange();
41734             }
41735         } else {
41736             return this.doc.createRange();
41737         }
41738     },
41739     getParentElement: function()
41740     {
41741         
41742         this.assignDocWin();
41743         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41744         
41745         var range = this.createRange(sel);
41746          
41747         try {
41748             var p = range.commonAncestorContainer;
41749             while (p.nodeType == 3) { // text node
41750                 p = p.parentNode;
41751             }
41752             return p;
41753         } catch (e) {
41754             return null;
41755         }
41756     
41757     },
41758     /***
41759      *
41760      * Range intersection.. the hard stuff...
41761      *  '-1' = before
41762      *  '0' = hits..
41763      *  '1' = after.
41764      *         [ -- selected range --- ]
41765      *   [fail]                        [fail]
41766      *
41767      *    basically..
41768      *      if end is before start or  hits it. fail.
41769      *      if start is after end or hits it fail.
41770      *
41771      *   if either hits (but other is outside. - then it's not 
41772      *   
41773      *    
41774      **/
41775     
41776     
41777     // @see http://www.thismuchiknow.co.uk/?p=64.
41778     rangeIntersectsNode : function(range, node)
41779     {
41780         var nodeRange = node.ownerDocument.createRange();
41781         try {
41782             nodeRange.selectNode(node);
41783         } catch (e) {
41784             nodeRange.selectNodeContents(node);
41785         }
41786     
41787         var rangeStartRange = range.cloneRange();
41788         rangeStartRange.collapse(true);
41789     
41790         var rangeEndRange = range.cloneRange();
41791         rangeEndRange.collapse(false);
41792     
41793         var nodeStartRange = nodeRange.cloneRange();
41794         nodeStartRange.collapse(true);
41795     
41796         var nodeEndRange = nodeRange.cloneRange();
41797         nodeEndRange.collapse(false);
41798     
41799         return rangeStartRange.compareBoundaryPoints(
41800                  Range.START_TO_START, nodeEndRange) == -1 &&
41801                rangeEndRange.compareBoundaryPoints(
41802                  Range.START_TO_START, nodeStartRange) == 1;
41803         
41804          
41805     },
41806     rangeCompareNode : function(range, node)
41807     {
41808         var nodeRange = node.ownerDocument.createRange();
41809         try {
41810             nodeRange.selectNode(node);
41811         } catch (e) {
41812             nodeRange.selectNodeContents(node);
41813         }
41814         
41815         
41816         range.collapse(true);
41817     
41818         nodeRange.collapse(true);
41819      
41820         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41821         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41822          
41823         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41824         
41825         var nodeIsBefore   =  ss == 1;
41826         var nodeIsAfter    = ee == -1;
41827         
41828         if (nodeIsBefore && nodeIsAfter)
41829             return 0; // outer
41830         if (!nodeIsBefore && nodeIsAfter)
41831             return 1; //right trailed.
41832         
41833         if (nodeIsBefore && !nodeIsAfter)
41834             return 2;  // left trailed.
41835         // fully contined.
41836         return 3;
41837     },
41838
41839     // private? - in a new class?
41840     cleanUpPaste :  function()
41841     {
41842         // cleans up the whole document..
41843          Roo.log('cleanuppaste');
41844         this.cleanUpChildren(this.doc.body);
41845         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41846         if (clean != this.doc.body.innerHTML) {
41847             this.doc.body.innerHTML = clean;
41848         }
41849         
41850     },
41851     
41852     cleanWordChars : function(input) {// change the chars to hex code
41853         var he = Roo.form.HtmlEditor;
41854         
41855         var output = input;
41856         Roo.each(he.swapCodes, function(sw) { 
41857             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41858             
41859             output = output.replace(swapper, sw[1]);
41860         });
41861         
41862         return output;
41863     },
41864     
41865     
41866     cleanUpChildren : function (n)
41867     {
41868         if (!n.childNodes.length) {
41869             return;
41870         }
41871         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41872            this.cleanUpChild(n.childNodes[i]);
41873         }
41874     },
41875     
41876     
41877         
41878     
41879     cleanUpChild : function (node)
41880     {
41881         var ed = this;
41882         //console.log(node);
41883         if (node.nodeName == "#text") {
41884             // clean up silly Windows -- stuff?
41885             return; 
41886         }
41887         if (node.nodeName == "#comment") {
41888             node.parentNode.removeChild(node);
41889             // clean up silly Windows -- stuff?
41890             return; 
41891         }
41892         
41893         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41894             // remove node.
41895             node.parentNode.removeChild(node);
41896             return;
41897             
41898         }
41899         
41900         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41901         
41902         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41903         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41904         
41905         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41906         //    remove_keep_children = true;
41907         //}
41908         
41909         if (remove_keep_children) {
41910             this.cleanUpChildren(node);
41911             // inserts everything just before this node...
41912             while (node.childNodes.length) {
41913                 var cn = node.childNodes[0];
41914                 node.removeChild(cn);
41915                 node.parentNode.insertBefore(cn, node);
41916             }
41917             node.parentNode.removeChild(node);
41918             return;
41919         }
41920         
41921         if (!node.attributes || !node.attributes.length) {
41922             this.cleanUpChildren(node);
41923             return;
41924         }
41925         
41926         function cleanAttr(n,v)
41927         {
41928             
41929             if (v.match(/^\./) || v.match(/^\//)) {
41930                 return;
41931             }
41932             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41933                 return;
41934             }
41935             if (v.match(/^#/)) {
41936                 return;
41937             }
41938 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41939             node.removeAttribute(n);
41940             
41941         }
41942         
41943         function cleanStyle(n,v)
41944         {
41945             if (v.match(/expression/)) { //XSS?? should we even bother..
41946                 node.removeAttribute(n);
41947                 return;
41948             }
41949             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41950             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41951             
41952             
41953             var parts = v.split(/;/);
41954             var clean = [];
41955             
41956             Roo.each(parts, function(p) {
41957                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41958                 if (!p.length) {
41959                     return true;
41960                 }
41961                 var l = p.split(':').shift().replace(/\s+/g,'');
41962                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41963                 
41964                 
41965                 if ( cblack.indexOf(l) > -1) {
41966 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41967                     //node.removeAttribute(n);
41968                     return true;
41969                 }
41970                 //Roo.log()
41971                 // only allow 'c whitelisted system attributes'
41972                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41973 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41974                     //node.removeAttribute(n);
41975                     return true;
41976                 }
41977                 
41978                 
41979                  
41980                 
41981                 clean.push(p);
41982                 return true;
41983             });
41984             if (clean.length) { 
41985                 node.setAttribute(n, clean.join(';'));
41986             } else {
41987                 node.removeAttribute(n);
41988             }
41989             
41990         }
41991         
41992         
41993         for (var i = node.attributes.length-1; i > -1 ; i--) {
41994             var a = node.attributes[i];
41995             //console.log(a);
41996             
41997             if (a.name.toLowerCase().substr(0,2)=='on')  {
41998                 node.removeAttribute(a.name);
41999                 continue;
42000             }
42001             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
42002                 node.removeAttribute(a.name);
42003                 continue;
42004             }
42005             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
42006                 cleanAttr(a.name,a.value); // fixme..
42007                 continue;
42008             }
42009             if (a.name == 'style') {
42010                 cleanStyle(a.name,a.value);
42011                 continue;
42012             }
42013             /// clean up MS crap..
42014             // tecnically this should be a list of valid class'es..
42015             
42016             
42017             if (a.name == 'class') {
42018                 if (a.value.match(/^Mso/)) {
42019                     node.className = '';
42020                 }
42021                 
42022                 if (a.value.match(/body/)) {
42023                     node.className = '';
42024                 }
42025                 continue;
42026             }
42027             
42028             // style cleanup!?
42029             // class cleanup?
42030             
42031         }
42032         
42033         
42034         this.cleanUpChildren(node);
42035         
42036         
42037     }
42038     
42039     
42040     // hide stuff that is not compatible
42041     /**
42042      * @event blur
42043      * @hide
42044      */
42045     /**
42046      * @event change
42047      * @hide
42048      */
42049     /**
42050      * @event focus
42051      * @hide
42052      */
42053     /**
42054      * @event specialkey
42055      * @hide
42056      */
42057     /**
42058      * @cfg {String} fieldClass @hide
42059      */
42060     /**
42061      * @cfg {String} focusClass @hide
42062      */
42063     /**
42064      * @cfg {String} autoCreate @hide
42065      */
42066     /**
42067      * @cfg {String} inputType @hide
42068      */
42069     /**
42070      * @cfg {String} invalidClass @hide
42071      */
42072     /**
42073      * @cfg {String} invalidText @hide
42074      */
42075     /**
42076      * @cfg {String} msgFx @hide
42077      */
42078     /**
42079      * @cfg {String} validateOnBlur @hide
42080      */
42081 });
42082
42083 Roo.form.HtmlEditor.white = [
42084         'area', 'br', 'img', 'input', 'hr', 'wbr',
42085         
42086        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42087        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42088        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42089        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42090        'table',   'ul',         'xmp', 
42091        
42092        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42093       'thead',   'tr', 
42094      
42095       'dir', 'menu', 'ol', 'ul', 'dl',
42096        
42097       'embed',  'object'
42098 ];
42099
42100
42101 Roo.form.HtmlEditor.black = [
42102     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42103         'applet', // 
42104         'base',   'basefont', 'bgsound', 'blink',  'body', 
42105         'frame',  'frameset', 'head',    'html',   'ilayer', 
42106         'iframe', 'layer',  'link',     'meta',    'object',   
42107         'script', 'style' ,'title',  'xml' // clean later..
42108 ];
42109 Roo.form.HtmlEditor.clean = [
42110     'script', 'style', 'title', 'xml'
42111 ];
42112 Roo.form.HtmlEditor.remove = [
42113     'font'
42114 ];
42115 // attributes..
42116
42117 Roo.form.HtmlEditor.ablack = [
42118     'on'
42119 ];
42120     
42121 Roo.form.HtmlEditor.aclean = [ 
42122     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42123 ];
42124
42125 // protocols..
42126 Roo.form.HtmlEditor.pwhite= [
42127         'http',  'https',  'mailto'
42128 ];
42129
42130 // white listed style attributes.
42131 Roo.form.HtmlEditor.cwhite= [
42132       //  'text-align', /// default is to allow most things..
42133       
42134          
42135 //        'font-size'//??
42136 ];
42137
42138 // black listed style attributes.
42139 Roo.form.HtmlEditor.cblack= [
42140       //  'font-size' -- this can be set by the project 
42141 ];
42142
42143
42144 Roo.form.HtmlEditor.swapCodes   =[ 
42145     [    8211, "--" ], 
42146     [    8212, "--" ], 
42147     [    8216,  "'" ],  
42148     [    8217, "'" ],  
42149     [    8220, '"' ],  
42150     [    8221, '"' ],  
42151     [    8226, "*" ],  
42152     [    8230, "..." ]
42153 ]; 
42154
42155     // <script type="text/javascript">
42156 /*
42157  * Based on
42158  * Ext JS Library 1.1.1
42159  * Copyright(c) 2006-2007, Ext JS, LLC.
42160  *  
42161  
42162  */
42163
42164 /**
42165  * @class Roo.form.HtmlEditorToolbar1
42166  * Basic Toolbar
42167  * 
42168  * Usage:
42169  *
42170  new Roo.form.HtmlEditor({
42171     ....
42172     toolbars : [
42173         new Roo.form.HtmlEditorToolbar1({
42174             disable : { fonts: 1 , format: 1, ..., ... , ...],
42175             btns : [ .... ]
42176         })
42177     }
42178      
42179  * 
42180  * @cfg {Object} disable List of elements to disable..
42181  * @cfg {Array} btns List of additional buttons.
42182  * 
42183  * 
42184  * NEEDS Extra CSS? 
42185  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42186  */
42187  
42188 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42189 {
42190     
42191     Roo.apply(this, config);
42192     
42193     // default disabled, based on 'good practice'..
42194     this.disable = this.disable || {};
42195     Roo.applyIf(this.disable, {
42196         fontSize : true,
42197         colors : true,
42198         specialElements : true
42199     });
42200     
42201     
42202     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42203     // dont call parent... till later.
42204 }
42205
42206 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42207     
42208     tb: false,
42209     
42210     rendered: false,
42211     
42212     editor : false,
42213     /**
42214      * @cfg {Object} disable  List of toolbar elements to disable
42215          
42216      */
42217     disable : false,
42218       /**
42219      * @cfg {Array} fontFamilies An array of available font families
42220      */
42221     fontFamilies : [
42222         'Arial',
42223         'Courier New',
42224         'Tahoma',
42225         'Times New Roman',
42226         'Verdana'
42227     ],
42228     
42229     specialChars : [
42230            "&#169;",
42231           "&#174;",     
42232           "&#8482;",    
42233           "&#163;" ,    
42234          // "&#8212;",    
42235           "&#8230;",    
42236           "&#247;" ,    
42237         //  "&#225;" ,     ?? a acute?
42238            "&#8364;"    , //Euro
42239        //   "&#8220;"    ,
42240         //  "&#8221;"    ,
42241         //  "&#8226;"    ,
42242           "&#176;"  //   , // degrees
42243
42244          // "&#233;"     , // e ecute
42245          // "&#250;"     , // u ecute?
42246     ],
42247     
42248     specialElements : [
42249         {
42250             text: "Insert Table",
42251             xtype: 'MenuItem',
42252             xns : Roo.Menu,
42253             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42254                 
42255         },
42256         {    
42257             text: "Insert Image",
42258             xtype: 'MenuItem',
42259             xns : Roo.Menu,
42260             ihtml : '<img src="about:blank"/>'
42261             
42262         }
42263         
42264          
42265     ],
42266     
42267     
42268     inputElements : [ 
42269             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42270             "input:submit", "input:button", "select", "textarea", "label" ],
42271     formats : [
42272         ["p"] ,  
42273         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42274         ["pre"],[ "code"], 
42275         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42276         ['div'],['span']
42277     ],
42278     
42279     cleanStyles : [
42280         "font-size"
42281     ],
42282      /**
42283      * @cfg {String} defaultFont default font to use.
42284      */
42285     defaultFont: 'tahoma',
42286    
42287     fontSelect : false,
42288     
42289     
42290     formatCombo : false,
42291     
42292     init : function(editor)
42293     {
42294         this.editor = editor;
42295         
42296         
42297         var fid = editor.frameId;
42298         var etb = this;
42299         function btn(id, toggle, handler){
42300             var xid = fid + '-'+ id ;
42301             return {
42302                 id : xid,
42303                 cmd : id,
42304                 cls : 'x-btn-icon x-edit-'+id,
42305                 enableToggle:toggle !== false,
42306                 scope: editor, // was editor...
42307                 handler:handler||editor.relayBtnCmd,
42308                 clickEvent:'mousedown',
42309                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42310                 tabIndex:-1
42311             };
42312         }
42313         
42314         
42315         
42316         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42317         this.tb = tb;
42318          // stop form submits
42319         tb.el.on('click', function(e){
42320             e.preventDefault(); // what does this do?
42321         });
42322
42323         if(!this.disable.font) { // && !Roo.isSafari){
42324             /* why no safari for fonts 
42325             editor.fontSelect = tb.el.createChild({
42326                 tag:'select',
42327                 tabIndex: -1,
42328                 cls:'x-font-select',
42329                 html: this.createFontOptions()
42330             });
42331             
42332             editor.fontSelect.on('change', function(){
42333                 var font = editor.fontSelect.dom.value;
42334                 editor.relayCmd('fontname', font);
42335                 editor.deferFocus();
42336             }, editor);
42337             
42338             tb.add(
42339                 editor.fontSelect.dom,
42340                 '-'
42341             );
42342             */
42343             
42344         };
42345         if(!this.disable.formats){
42346             this.formatCombo = new Roo.form.ComboBox({
42347                 store: new Roo.data.SimpleStore({
42348                     id : 'tag',
42349                     fields: ['tag'],
42350                     data : this.formats // from states.js
42351                 }),
42352                 blockFocus : true,
42353                 name : '',
42354                 //autoCreate : {tag: "div",  size: "20"},
42355                 displayField:'tag',
42356                 typeAhead: false,
42357                 mode: 'local',
42358                 editable : false,
42359                 triggerAction: 'all',
42360                 emptyText:'Add tag',
42361                 selectOnFocus:true,
42362                 width:135,
42363                 listeners : {
42364                     'select': function(c, r, i) {
42365                         editor.insertTag(r.get('tag'));
42366                         editor.focus();
42367                     }
42368                 }
42369
42370             });
42371             tb.addField(this.formatCombo);
42372             
42373         }
42374         
42375         if(!this.disable.format){
42376             tb.add(
42377                 btn('bold'),
42378                 btn('italic'),
42379                 btn('underline')
42380             );
42381         };
42382         if(!this.disable.fontSize){
42383             tb.add(
42384                 '-',
42385                 
42386                 
42387                 btn('increasefontsize', false, editor.adjustFont),
42388                 btn('decreasefontsize', false, editor.adjustFont)
42389             );
42390         };
42391         
42392         
42393         if(!this.disable.colors){
42394             tb.add(
42395                 '-', {
42396                     id:editor.frameId +'-forecolor',
42397                     cls:'x-btn-icon x-edit-forecolor',
42398                     clickEvent:'mousedown',
42399                     tooltip: this.buttonTips['forecolor'] || undefined,
42400                     tabIndex:-1,
42401                     menu : new Roo.menu.ColorMenu({
42402                         allowReselect: true,
42403                         focus: Roo.emptyFn,
42404                         value:'000000',
42405                         plain:true,
42406                         selectHandler: function(cp, color){
42407                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42408                             editor.deferFocus();
42409                         },
42410                         scope: editor,
42411                         clickEvent:'mousedown'
42412                     })
42413                 }, {
42414                     id:editor.frameId +'backcolor',
42415                     cls:'x-btn-icon x-edit-backcolor',
42416                     clickEvent:'mousedown',
42417                     tooltip: this.buttonTips['backcolor'] || undefined,
42418                     tabIndex:-1,
42419                     menu : new Roo.menu.ColorMenu({
42420                         focus: Roo.emptyFn,
42421                         value:'FFFFFF',
42422                         plain:true,
42423                         allowReselect: true,
42424                         selectHandler: function(cp, color){
42425                             if(Roo.isGecko){
42426                                 editor.execCmd('useCSS', false);
42427                                 editor.execCmd('hilitecolor', color);
42428                                 editor.execCmd('useCSS', true);
42429                                 editor.deferFocus();
42430                             }else{
42431                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42432                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42433                                 editor.deferFocus();
42434                             }
42435                         },
42436                         scope:editor,
42437                         clickEvent:'mousedown'
42438                     })
42439                 }
42440             );
42441         };
42442         // now add all the items...
42443         
42444
42445         if(!this.disable.alignments){
42446             tb.add(
42447                 '-',
42448                 btn('justifyleft'),
42449                 btn('justifycenter'),
42450                 btn('justifyright')
42451             );
42452         };
42453
42454         //if(!Roo.isSafari){
42455             if(!this.disable.links){
42456                 tb.add(
42457                     '-',
42458                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
42459                 );
42460             };
42461
42462             if(!this.disable.lists){
42463                 tb.add(
42464                     '-',
42465                     btn('insertorderedlist'),
42466                     btn('insertunorderedlist')
42467                 );
42468             }
42469             if(!this.disable.sourceEdit){
42470                 tb.add(
42471                     '-',
42472                     btn('sourceedit', true, function(btn){
42473                         this.toggleSourceEdit(btn.pressed);
42474                     })
42475                 );
42476             }
42477         //}
42478         
42479         var smenu = { };
42480         // special menu.. - needs to be tidied up..
42481         if (!this.disable.special) {
42482             smenu = {
42483                 text: "&#169;",
42484                 cls: 'x-edit-none',
42485                 
42486                 menu : {
42487                     items : []
42488                 }
42489             };
42490             for (var i =0; i < this.specialChars.length; i++) {
42491                 smenu.menu.items.push({
42492                     
42493                     html: this.specialChars[i],
42494                     handler: function(a,b) {
42495                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42496                         //editor.insertAtCursor(a.html);
42497                         
42498                     },
42499                     tabIndex:-1
42500                 });
42501             }
42502             
42503             
42504             tb.add(smenu);
42505             
42506             
42507         }
42508         
42509         var cmenu = { };
42510         if (!this.disable.cleanStyles) {
42511             cmenu = {
42512                 cls: 'x-btn-icon x-btn-clear',
42513                 
42514                 menu : {
42515                     items : []
42516                 }
42517             };
42518             for (var i =0; i < this.cleanStyles.length; i++) {
42519                 cmenu.menu.items.push({
42520                     actiontype : this.cleanStyles[i],
42521                     html: 'Remove ' + this.cleanStyles[i],
42522                     handler: function(a,b) {
42523                         Roo.log(a);
42524                         Roo.log(b);
42525                         var c = Roo.get(editor.doc.body);
42526                         c.select('[style]').each(function(s) {
42527                             s.dom.style.removeProperty(a.actiontype);
42528                         });
42529                         
42530                     },
42531                     tabIndex:-1
42532                 });
42533             }
42534             
42535             tb.add(cmenu);
42536         }
42537          
42538         if (!this.disable.specialElements) {
42539             var semenu = {
42540                 text: "Other;",
42541                 cls: 'x-edit-none',
42542                 menu : {
42543                     items : []
42544                 }
42545             };
42546             for (var i =0; i < this.specialElements.length; i++) {
42547                 semenu.menu.items.push(
42548                     Roo.apply({ 
42549                         handler: function(a,b) {
42550                             editor.insertAtCursor(this.ihtml);
42551                         }
42552                     }, this.specialElements[i])
42553                 );
42554                     
42555             }
42556             
42557             tb.add(semenu);
42558             
42559             
42560         }
42561          
42562         
42563         if (this.btns) {
42564             for(var i =0; i< this.btns.length;i++) {
42565                 var b = Roo.factory(this.btns[i],Roo.form);
42566                 b.cls =  'x-edit-none';
42567                 b.scope = editor;
42568                 tb.add(b);
42569             }
42570         
42571         }
42572         
42573         
42574         
42575         // disable everything...
42576         
42577         this.tb.items.each(function(item){
42578            if(item.id != editor.frameId+ '-sourceedit'){
42579                 item.disable();
42580             }
42581         });
42582         this.rendered = true;
42583         
42584         // the all the btns;
42585         editor.on('editorevent', this.updateToolbar, this);
42586         // other toolbars need to implement this..
42587         //editor.on('editmodechange', this.updateToolbar, this);
42588     },
42589     
42590     
42591     
42592     /**
42593      * Protected method that will not generally be called directly. It triggers
42594      * a toolbar update by reading the markup state of the current selection in the editor.
42595      */
42596     updateToolbar: function(){
42597
42598         if(!this.editor.activated){
42599             this.editor.onFirstFocus();
42600             return;
42601         }
42602
42603         var btns = this.tb.items.map, 
42604             doc = this.editor.doc,
42605             frameId = this.editor.frameId;
42606
42607         if(!this.disable.font && !Roo.isSafari){
42608             /*
42609             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42610             if(name != this.fontSelect.dom.value){
42611                 this.fontSelect.dom.value = name;
42612             }
42613             */
42614         }
42615         if(!this.disable.format){
42616             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42617             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42618             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42619         }
42620         if(!this.disable.alignments){
42621             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42622             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42623             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42624         }
42625         if(!Roo.isSafari && !this.disable.lists){
42626             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42627             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42628         }
42629         
42630         var ans = this.editor.getAllAncestors();
42631         if (this.formatCombo) {
42632             
42633             
42634             var store = this.formatCombo.store;
42635             this.formatCombo.setValue("");
42636             for (var i =0; i < ans.length;i++) {
42637                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42638                     // select it..
42639                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42640                     break;
42641                 }
42642             }
42643         }
42644         
42645         
42646         
42647         // hides menus... - so this cant be on a menu...
42648         Roo.menu.MenuMgr.hideAll();
42649
42650         //this.editorsyncValue();
42651     },
42652    
42653     
42654     createFontOptions : function(){
42655         var buf = [], fs = this.fontFamilies, ff, lc;
42656         
42657         
42658         
42659         for(var i = 0, len = fs.length; i< len; i++){
42660             ff = fs[i];
42661             lc = ff.toLowerCase();
42662             buf.push(
42663                 '<option value="',lc,'" style="font-family:',ff,';"',
42664                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42665                     ff,
42666                 '</option>'
42667             );
42668         }
42669         return buf.join('');
42670     },
42671     
42672     toggleSourceEdit : function(sourceEditMode){
42673         if(sourceEditMode === undefined){
42674             sourceEditMode = !this.sourceEditMode;
42675         }
42676         this.sourceEditMode = sourceEditMode === true;
42677         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42678         // just toggle the button?
42679         if(btn.pressed !== this.editor.sourceEditMode){
42680             btn.toggle(this.editor.sourceEditMode);
42681             return;
42682         }
42683         
42684         if(this.sourceEditMode){
42685             this.tb.items.each(function(item){
42686                 if(item.cmd != 'sourceedit'){
42687                     item.disable();
42688                 }
42689             });
42690           
42691         }else{
42692             if(this.initialized){
42693                 this.tb.items.each(function(item){
42694                     item.enable();
42695                 });
42696             }
42697             
42698         }
42699         // tell the editor that it's been pressed..
42700         this.editor.toggleSourceEdit(sourceEditMode);
42701        
42702     },
42703      /**
42704      * Object collection of toolbar tooltips for the buttons in the editor. The key
42705      * is the command id associated with that button and the value is a valid QuickTips object.
42706      * For example:
42707 <pre><code>
42708 {
42709     bold : {
42710         title: 'Bold (Ctrl+B)',
42711         text: 'Make the selected text bold.',
42712         cls: 'x-html-editor-tip'
42713     },
42714     italic : {
42715         title: 'Italic (Ctrl+I)',
42716         text: 'Make the selected text italic.',
42717         cls: 'x-html-editor-tip'
42718     },
42719     ...
42720 </code></pre>
42721     * @type Object
42722      */
42723     buttonTips : {
42724         bold : {
42725             title: 'Bold (Ctrl+B)',
42726             text: 'Make the selected text bold.',
42727             cls: 'x-html-editor-tip'
42728         },
42729         italic : {
42730             title: 'Italic (Ctrl+I)',
42731             text: 'Make the selected text italic.',
42732             cls: 'x-html-editor-tip'
42733         },
42734         underline : {
42735             title: 'Underline (Ctrl+U)',
42736             text: 'Underline the selected text.',
42737             cls: 'x-html-editor-tip'
42738         },
42739         increasefontsize : {
42740             title: 'Grow Text',
42741             text: 'Increase the font size.',
42742             cls: 'x-html-editor-tip'
42743         },
42744         decreasefontsize : {
42745             title: 'Shrink Text',
42746             text: 'Decrease the font size.',
42747             cls: 'x-html-editor-tip'
42748         },
42749         backcolor : {
42750             title: 'Text Highlight Color',
42751             text: 'Change the background color of the selected text.',
42752             cls: 'x-html-editor-tip'
42753         },
42754         forecolor : {
42755             title: 'Font Color',
42756             text: 'Change the color of the selected text.',
42757             cls: 'x-html-editor-tip'
42758         },
42759         justifyleft : {
42760             title: 'Align Text Left',
42761             text: 'Align text to the left.',
42762             cls: 'x-html-editor-tip'
42763         },
42764         justifycenter : {
42765             title: 'Center Text',
42766             text: 'Center text in the editor.',
42767             cls: 'x-html-editor-tip'
42768         },
42769         justifyright : {
42770             title: 'Align Text Right',
42771             text: 'Align text to the right.',
42772             cls: 'x-html-editor-tip'
42773         },
42774         insertunorderedlist : {
42775             title: 'Bullet List',
42776             text: 'Start a bulleted list.',
42777             cls: 'x-html-editor-tip'
42778         },
42779         insertorderedlist : {
42780             title: 'Numbered List',
42781             text: 'Start a numbered list.',
42782             cls: 'x-html-editor-tip'
42783         },
42784         createlink : {
42785             title: 'Hyperlink',
42786             text: 'Make the selected text a hyperlink.',
42787             cls: 'x-html-editor-tip'
42788         },
42789         sourceedit : {
42790             title: 'Source Edit',
42791             text: 'Switch to source editing mode.',
42792             cls: 'x-html-editor-tip'
42793         }
42794     },
42795     // private
42796     onDestroy : function(){
42797         if(this.rendered){
42798             
42799             this.tb.items.each(function(item){
42800                 if(item.menu){
42801                     item.menu.removeAll();
42802                     if(item.menu.el){
42803                         item.menu.el.destroy();
42804                     }
42805                 }
42806                 item.destroy();
42807             });
42808              
42809         }
42810     },
42811     onFirstFocus: function() {
42812         this.tb.items.each(function(item){
42813            item.enable();
42814         });
42815     }
42816 });
42817
42818
42819
42820
42821 // <script type="text/javascript">
42822 /*
42823  * Based on
42824  * Ext JS Library 1.1.1
42825  * Copyright(c) 2006-2007, Ext JS, LLC.
42826  *  
42827  
42828  */
42829
42830  
42831 /**
42832  * @class Roo.form.HtmlEditor.ToolbarContext
42833  * Context Toolbar
42834  * 
42835  * Usage:
42836  *
42837  new Roo.form.HtmlEditor({
42838     ....
42839     toolbars : [
42840         { xtype: 'ToolbarStandard', styles : {} }
42841         { xtype: 'ToolbarContext', disable : {} }
42842     ]
42843 })
42844
42845      
42846  * 
42847  * @config : {Object} disable List of elements to disable.. (not done yet.)
42848  * @config : {Object} styles  Map of styles available.
42849  * 
42850  */
42851
42852 Roo.form.HtmlEditor.ToolbarContext = function(config)
42853 {
42854     
42855     Roo.apply(this, config);
42856     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42857     // dont call parent... till later.
42858     this.styles = this.styles || {};
42859 }
42860
42861  
42862
42863 Roo.form.HtmlEditor.ToolbarContext.types = {
42864     'IMG' : {
42865         width : {
42866             title: "Width",
42867             width: 40
42868         },
42869         height:  {
42870             title: "Height",
42871             width: 40
42872         },
42873         align: {
42874             title: "Align",
42875             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42876             width : 80
42877             
42878         },
42879         border: {
42880             title: "Border",
42881             width: 40
42882         },
42883         alt: {
42884             title: "Alt",
42885             width: 120
42886         },
42887         src : {
42888             title: "Src",
42889             width: 220
42890         }
42891         
42892     },
42893     'A' : {
42894         name : {
42895             title: "Name",
42896             width: 50
42897         },
42898         target:  {
42899             title: "Target",
42900             width: 120
42901         },
42902         href:  {
42903             title: "Href",
42904             width: 220
42905         } // border?
42906         
42907     },
42908     'TABLE' : {
42909         rows : {
42910             title: "Rows",
42911             width: 20
42912         },
42913         cols : {
42914             title: "Cols",
42915             width: 20
42916         },
42917         width : {
42918             title: "Width",
42919             width: 40
42920         },
42921         height : {
42922             title: "Height",
42923             width: 40
42924         },
42925         border : {
42926             title: "Border",
42927             width: 20
42928         }
42929     },
42930     'TD' : {
42931         width : {
42932             title: "Width",
42933             width: 40
42934         },
42935         height : {
42936             title: "Height",
42937             width: 40
42938         },   
42939         align: {
42940             title: "Align",
42941             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42942             width: 80
42943         },
42944         valign: {
42945             title: "Valign",
42946             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42947             width: 80
42948         },
42949         colspan: {
42950             title: "Colspan",
42951             width: 20
42952             
42953         },
42954          'font-family'  : {
42955             title : "Font",
42956             style : 'fontFamily',
42957             displayField: 'display',
42958             optname : 'font-family',
42959             width: 140
42960         }
42961     },
42962     'INPUT' : {
42963         name : {
42964             title: "name",
42965             width: 120
42966         },
42967         value : {
42968             title: "Value",
42969             width: 120
42970         },
42971         width : {
42972             title: "Width",
42973             width: 40
42974         }
42975     },
42976     'LABEL' : {
42977         'for' : {
42978             title: "For",
42979             width: 120
42980         }
42981     },
42982     'TEXTAREA' : {
42983           name : {
42984             title: "name",
42985             width: 120
42986         },
42987         rows : {
42988             title: "Rows",
42989             width: 20
42990         },
42991         cols : {
42992             title: "Cols",
42993             width: 20
42994         }
42995     },
42996     'SELECT' : {
42997         name : {
42998             title: "name",
42999             width: 120
43000         },
43001         selectoptions : {
43002             title: "Options",
43003             width: 200
43004         }
43005     },
43006     
43007     // should we really allow this??
43008     // should this just be 
43009     'BODY' : {
43010         title : {
43011             title: "Title",
43012             width: 200,
43013             disabled : true
43014         }
43015     },
43016     'SPAN' : {
43017         'font-family'  : {
43018             title : "Font",
43019             style : 'fontFamily',
43020             displayField: 'display',
43021             optname : 'font-family',
43022             width: 140
43023         }
43024     },
43025     'DIV' : {
43026         'font-family'  : {
43027             title : "Font",
43028             style : 'fontFamily',
43029             displayField: 'display',
43030             optname : 'font-family',
43031             width: 140
43032         }
43033     },
43034      'P' : {
43035         'font-family'  : {
43036             title : "Font",
43037             style : 'fontFamily',
43038             displayField: 'display',
43039             optname : 'font-family',
43040             width: 140
43041         }
43042     },
43043     
43044     '*' : {
43045         // empty..
43046     }
43047
43048 };
43049
43050 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43051 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43052
43053 Roo.form.HtmlEditor.ToolbarContext.options = {
43054         'font-family'  : [ 
43055                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43056                 [ 'Courier New', 'Courier New'],
43057                 [ 'Tahoma', 'Tahoma'],
43058                 [ 'Times New Roman,serif', 'Times'],
43059                 [ 'Verdana','Verdana' ]
43060         ]
43061 };
43062
43063 // fixme - these need to be configurable..
43064  
43065
43066 Roo.form.HtmlEditor.ToolbarContext.types
43067
43068
43069 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43070     
43071     tb: false,
43072     
43073     rendered: false,
43074     
43075     editor : false,
43076     /**
43077      * @cfg {Object} disable  List of toolbar elements to disable
43078          
43079      */
43080     disable : false,
43081     /**
43082      * @cfg {Object} styles List of styles 
43083      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43084      *
43085      * These must be defined in the page, so they get rendered correctly..
43086      * .headline { }
43087      * TD.underline { }
43088      * 
43089      */
43090     styles : false,
43091     
43092     options: false,
43093     
43094     toolbars : false,
43095     
43096     init : function(editor)
43097     {
43098         this.editor = editor;
43099         
43100         
43101         var fid = editor.frameId;
43102         var etb = this;
43103         function btn(id, toggle, handler){
43104             var xid = fid + '-'+ id ;
43105             return {
43106                 id : xid,
43107                 cmd : id,
43108                 cls : 'x-btn-icon x-edit-'+id,
43109                 enableToggle:toggle !== false,
43110                 scope: editor, // was editor...
43111                 handler:handler||editor.relayBtnCmd,
43112                 clickEvent:'mousedown',
43113                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43114                 tabIndex:-1
43115             };
43116         }
43117         // create a new element.
43118         var wdiv = editor.wrap.createChild({
43119                 tag: 'div'
43120             }, editor.wrap.dom.firstChild.nextSibling, true);
43121         
43122         // can we do this more than once??
43123         
43124          // stop form submits
43125       
43126  
43127         // disable everything...
43128         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43129         this.toolbars = {};
43130            
43131         for (var i in  ty) {
43132           
43133             this.toolbars[i] = this.buildToolbar(ty[i],i);
43134         }
43135         this.tb = this.toolbars.BODY;
43136         this.tb.el.show();
43137         this.buildFooter();
43138         this.footer.show();
43139         editor.on('hide', function( ) { this.footer.hide() }, this);
43140         editor.on('show', function( ) { this.footer.show() }, this);
43141         
43142          
43143         this.rendered = true;
43144         
43145         // the all the btns;
43146         editor.on('editorevent', this.updateToolbar, this);
43147         // other toolbars need to implement this..
43148         //editor.on('editmodechange', this.updateToolbar, this);
43149     },
43150     
43151     
43152     
43153     /**
43154      * Protected method that will not generally be called directly. It triggers
43155      * a toolbar update by reading the markup state of the current selection in the editor.
43156      */
43157     updateToolbar: function(editor,ev,sel){
43158
43159         //Roo.log(ev);
43160         // capture mouse up - this is handy for selecting images..
43161         // perhaps should go somewhere else...
43162         if(!this.editor.activated){
43163              this.editor.onFirstFocus();
43164             return;
43165         }
43166         
43167         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43168         // selectNode - might want to handle IE?
43169         if (ev &&
43170             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43171             ev.target && ev.target.tagName == 'IMG') {
43172             // they have click on an image...
43173             // let's see if we can change the selection...
43174             sel = ev.target;
43175          
43176               var nodeRange = sel.ownerDocument.createRange();
43177             try {
43178                 nodeRange.selectNode(sel);
43179             } catch (e) {
43180                 nodeRange.selectNodeContents(sel);
43181             }
43182             //nodeRange.collapse(true);
43183             var s = editor.win.getSelection();
43184             s.removeAllRanges();
43185             s.addRange(nodeRange);
43186         }  
43187         
43188       
43189         var updateFooter = sel ? false : true;
43190         
43191         
43192         var ans = this.editor.getAllAncestors();
43193         
43194         // pick
43195         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43196         
43197         if (!sel) { 
43198             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
43199             sel = sel ? sel : this.editor.doc.body;
43200             sel = sel.tagName.length ? sel : this.editor.doc.body;
43201             
43202         }
43203         // pick a menu that exists..
43204         var tn = sel.tagName.toUpperCase();
43205         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43206         
43207         tn = sel.tagName.toUpperCase();
43208         
43209         var lastSel = this.tb.selectedNode
43210         
43211         this.tb.selectedNode = sel;
43212         
43213         // if current menu does not match..
43214         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43215                 
43216             this.tb.el.hide();
43217             ///console.log("show: " + tn);
43218             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43219             this.tb.el.show();
43220             // update name
43221             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43222             
43223             
43224             // update attributes
43225             if (this.tb.fields) {
43226                 this.tb.fields.each(function(e) {
43227                     if (e.stylename) {
43228                         e.setValue(sel.style[e.stylename]);
43229                         return;
43230                     } 
43231                    e.setValue(sel.getAttribute(e.attrname));
43232                 });
43233             }
43234             
43235             var hasStyles = false;
43236             for(var i in this.styles) {
43237                 hasStyles = true;
43238                 break;
43239             }
43240             
43241             // update styles
43242             if (hasStyles) { 
43243                 var st = this.tb.fields.item(0);
43244                 
43245                 st.store.removeAll();
43246                
43247                 
43248                 var cn = sel.className.split(/\s+/);
43249                 
43250                 var avs = [];
43251                 if (this.styles['*']) {
43252                     
43253                     Roo.each(this.styles['*'], function(v) {
43254                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43255                     });
43256                 }
43257                 if (this.styles[tn]) { 
43258                     Roo.each(this.styles[tn], function(v) {
43259                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43260                     });
43261                 }
43262                 
43263                 st.store.loadData(avs);
43264                 st.collapse();
43265                 st.setValue(cn);
43266             }
43267             // flag our selected Node.
43268             this.tb.selectedNode = sel;
43269            
43270            
43271             Roo.menu.MenuMgr.hideAll();
43272
43273         }
43274         
43275         if (!updateFooter) {
43276             //this.footDisp.dom.innerHTML = ''; 
43277             return;
43278         }
43279         // update the footer
43280         //
43281         var html = '';
43282         
43283         this.footerEls = ans.reverse();
43284         Roo.each(this.footerEls, function(a,i) {
43285             if (!a) { return; }
43286             html += html.length ? ' &gt; '  :  '';
43287             
43288             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43289             
43290         });
43291        
43292         // 
43293         var sz = this.footDisp.up('td').getSize();
43294         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43295         this.footDisp.dom.style.marginLeft = '5px';
43296         
43297         this.footDisp.dom.style.overflow = 'hidden';
43298         
43299         this.footDisp.dom.innerHTML = html;
43300             
43301         //this.editorsyncValue();
43302     },
43303      
43304     
43305    
43306        
43307     // private
43308     onDestroy : function(){
43309         if(this.rendered){
43310             
43311             this.tb.items.each(function(item){
43312                 if(item.menu){
43313                     item.menu.removeAll();
43314                     if(item.menu.el){
43315                         item.menu.el.destroy();
43316                     }
43317                 }
43318                 item.destroy();
43319             });
43320              
43321         }
43322     },
43323     onFirstFocus: function() {
43324         // need to do this for all the toolbars..
43325         this.tb.items.each(function(item){
43326            item.enable();
43327         });
43328     },
43329     buildToolbar: function(tlist, nm)
43330     {
43331         var editor = this.editor;
43332          // create a new element.
43333         var wdiv = editor.wrap.createChild({
43334                 tag: 'div'
43335             }, editor.wrap.dom.firstChild.nextSibling, true);
43336         
43337        
43338         var tb = new Roo.Toolbar(wdiv);
43339         // add the name..
43340         
43341         tb.add(nm+ ":&nbsp;");
43342         
43343         var styles = [];
43344         for(var i in this.styles) {
43345             styles.push(i);
43346         }
43347         
43348         // styles...
43349         if (styles && styles.length) {
43350             
43351             // this needs a multi-select checkbox...
43352             tb.addField( new Roo.form.ComboBox({
43353                 store: new Roo.data.SimpleStore({
43354                     id : 'val',
43355                     fields: ['val', 'selected'],
43356                     data : [] 
43357                 }),
43358                 name : '-roo-edit-className',
43359                 attrname : 'className',
43360                 displayField: 'val',
43361                 typeAhead: false,
43362                 mode: 'local',
43363                 editable : false,
43364                 triggerAction: 'all',
43365                 emptyText:'Select Style',
43366                 selectOnFocus:true,
43367                 width: 130,
43368                 listeners : {
43369                     'select': function(c, r, i) {
43370                         // initial support only for on class per el..
43371                         tb.selectedNode.className =  r ? r.get('val') : '';
43372                         editor.syncValue();
43373                     }
43374                 }
43375     
43376             }));
43377         }
43378         
43379         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43380         var tbops = tbc.options;
43381         
43382         for (var i in tlist) {
43383             
43384             var item = tlist[i];
43385             tb.add(item.title + ":&nbsp;");
43386             
43387             
43388             //optname == used so you can configure the options available..
43389             var opts = item.opts ? item.opts : false;
43390             if (item.optname) {
43391                 opts = tbops[item.optname];
43392            
43393             }
43394             
43395             if (opts) {
43396                 // opts == pulldown..
43397                 tb.addField( new Roo.form.ComboBox({
43398                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43399                         id : 'val',
43400                         fields: ['val', 'display'],
43401                         data : opts  
43402                     }),
43403                     name : '-roo-edit-' + i,
43404                     attrname : i,
43405                     stylename : item.style ? item.style : false,
43406                     displayField: item.displayField ? item.displayField : 'val',
43407                     valueField :  'val',
43408                     typeAhead: false,
43409                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43410                     editable : false,
43411                     triggerAction: 'all',
43412                     emptyText:'Select',
43413                     selectOnFocus:true,
43414                     width: item.width ? item.width  : 130,
43415                     listeners : {
43416                         'select': function(c, r, i) {
43417                             if (c.stylename) {
43418                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43419                                 return;
43420                             }
43421                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43422                         }
43423                     }
43424
43425                 }));
43426                 continue;
43427                     
43428                  
43429                 
43430                 tb.addField( new Roo.form.TextField({
43431                     name: i,
43432                     width: 100,
43433                     //allowBlank:false,
43434                     value: ''
43435                 }));
43436                 continue;
43437             }
43438             tb.addField( new Roo.form.TextField({
43439                 name: '-roo-edit-' + i,
43440                 attrname : i,
43441                 
43442                 width: item.width,
43443                 //allowBlank:true,
43444                 value: '',
43445                 listeners: {
43446                     'change' : function(f, nv, ov) {
43447                         tb.selectedNode.setAttribute(f.attrname, nv);
43448                     }
43449                 }
43450             }));
43451              
43452         }
43453         tb.addFill();
43454         var _this = this;
43455         tb.addButton( {
43456             text: 'Remove Tag',
43457     
43458             listeners : {
43459                 click : function ()
43460                 {
43461                     // remove
43462                     // undo does not work.
43463                      
43464                     var sn = tb.selectedNode;
43465                     
43466                     var pn = sn.parentNode;
43467                     
43468                     var stn =  sn.childNodes[0];
43469                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43470                     while (sn.childNodes.length) {
43471                         var node = sn.childNodes[0];
43472                         sn.removeChild(node);
43473                         //Roo.log(node);
43474                         pn.insertBefore(node, sn);
43475                         
43476                     }
43477                     pn.removeChild(sn);
43478                     var range = editor.createRange();
43479         
43480                     range.setStart(stn,0);
43481                     range.setEnd(en,0); //????
43482                     //range.selectNode(sel);
43483                     
43484                     
43485                     var selection = editor.getSelection();
43486                     selection.removeAllRanges();
43487                     selection.addRange(range);
43488                     
43489                     
43490                     
43491                     //_this.updateToolbar(null, null, pn);
43492                     _this.updateToolbar(null, null, null);
43493                     _this.footDisp.dom.innerHTML = ''; 
43494                 }
43495             }
43496             
43497                     
43498                 
43499             
43500         });
43501         
43502         
43503         tb.el.on('click', function(e){
43504             e.preventDefault(); // what does this do?
43505         });
43506         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43507         tb.el.hide();
43508         tb.name = nm;
43509         // dont need to disable them... as they will get hidden
43510         return tb;
43511          
43512         
43513     },
43514     buildFooter : function()
43515     {
43516         
43517         var fel = this.editor.wrap.createChild();
43518         this.footer = new Roo.Toolbar(fel);
43519         // toolbar has scrolly on left / right?
43520         var footDisp= new Roo.Toolbar.Fill();
43521         var _t = this;
43522         this.footer.add(
43523             {
43524                 text : '&lt;',
43525                 xtype: 'Button',
43526                 handler : function() {
43527                     _t.footDisp.scrollTo('left',0,true)
43528                 }
43529             }
43530         );
43531         this.footer.add( footDisp );
43532         this.footer.add( 
43533             {
43534                 text : '&gt;',
43535                 xtype: 'Button',
43536                 handler : function() {
43537                     // no animation..
43538                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43539                 }
43540             }
43541         );
43542         var fel = Roo.get(footDisp.el);
43543         fel.addClass('x-editor-context');
43544         this.footDispWrap = fel; 
43545         this.footDispWrap.overflow  = 'hidden';
43546         
43547         this.footDisp = fel.createChild();
43548         this.footDispWrap.on('click', this.onContextClick, this)
43549         
43550         
43551     },
43552     onContextClick : function (ev,dom)
43553     {
43554         ev.preventDefault();
43555         var  cn = dom.className;
43556         //Roo.log(cn);
43557         if (!cn.match(/x-ed-loc-/)) {
43558             return;
43559         }
43560         var n = cn.split('-').pop();
43561         var ans = this.footerEls;
43562         var sel = ans[n];
43563         
43564          // pick
43565         var range = this.editor.createRange();
43566         
43567         range.selectNodeContents(sel);
43568         //range.selectNode(sel);
43569         
43570         
43571         var selection = this.editor.getSelection();
43572         selection.removeAllRanges();
43573         selection.addRange(range);
43574         
43575         
43576         
43577         this.updateToolbar(null, null, sel);
43578         
43579         
43580     }
43581     
43582     
43583     
43584     
43585     
43586 });
43587
43588
43589
43590
43591
43592 /*
43593  * Based on:
43594  * Ext JS Library 1.1.1
43595  * Copyright(c) 2006-2007, Ext JS, LLC.
43596  *
43597  * Originally Released Under LGPL - original licence link has changed is not relivant.
43598  *
43599  * Fork - LGPL
43600  * <script type="text/javascript">
43601  */
43602  
43603 /**
43604  * @class Roo.form.BasicForm
43605  * @extends Roo.util.Observable
43606  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43607  * @constructor
43608  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43609  * @param {Object} config Configuration options
43610  */
43611 Roo.form.BasicForm = function(el, config){
43612     this.allItems = [];
43613     this.childForms = [];
43614     Roo.apply(this, config);
43615     /*
43616      * The Roo.form.Field items in this form.
43617      * @type MixedCollection
43618      */
43619      
43620      
43621     this.items = new Roo.util.MixedCollection(false, function(o){
43622         return o.id || (o.id = Roo.id());
43623     });
43624     this.addEvents({
43625         /**
43626          * @event beforeaction
43627          * Fires before any action is performed. Return false to cancel the action.
43628          * @param {Form} this
43629          * @param {Action} action The action to be performed
43630          */
43631         beforeaction: true,
43632         /**
43633          * @event actionfailed
43634          * Fires when an action fails.
43635          * @param {Form} this
43636          * @param {Action} action The action that failed
43637          */
43638         actionfailed : true,
43639         /**
43640          * @event actioncomplete
43641          * Fires when an action is completed.
43642          * @param {Form} this
43643          * @param {Action} action The action that completed
43644          */
43645         actioncomplete : true
43646     });
43647     if(el){
43648         this.initEl(el);
43649     }
43650     Roo.form.BasicForm.superclass.constructor.call(this);
43651 };
43652
43653 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43654     /**
43655      * @cfg {String} method
43656      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43657      */
43658     /**
43659      * @cfg {DataReader} reader
43660      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43661      * This is optional as there is built-in support for processing JSON.
43662      */
43663     /**
43664      * @cfg {DataReader} errorReader
43665      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43666      * This is completely optional as there is built-in support for processing JSON.
43667      */
43668     /**
43669      * @cfg {String} url
43670      * The URL to use for form actions if one isn't supplied in the action options.
43671      */
43672     /**
43673      * @cfg {Boolean} fileUpload
43674      * Set to true if this form is a file upload.
43675      */
43676      
43677     /**
43678      * @cfg {Object} baseParams
43679      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43680      */
43681      /**
43682      
43683     /**
43684      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43685      */
43686     timeout: 30,
43687
43688     // private
43689     activeAction : null,
43690
43691     /**
43692      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43693      * or setValues() data instead of when the form was first created.
43694      */
43695     trackResetOnLoad : false,
43696     
43697     
43698     /**
43699      * childForms - used for multi-tab forms
43700      * @type {Array}
43701      */
43702     childForms : false,
43703     
43704     /**
43705      * allItems - full list of fields.
43706      * @type {Array}
43707      */
43708     allItems : false,
43709     
43710     /**
43711      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43712      * element by passing it or its id or mask the form itself by passing in true.
43713      * @type Mixed
43714      */
43715     waitMsgTarget : false,
43716
43717     // private
43718     initEl : function(el){
43719         this.el = Roo.get(el);
43720         this.id = this.el.id || Roo.id();
43721         this.el.on('submit', this.onSubmit, this);
43722         this.el.addClass('x-form');
43723     },
43724
43725     // private
43726     onSubmit : function(e){
43727         e.stopEvent();
43728     },
43729
43730     /**
43731      * Returns true if client-side validation on the form is successful.
43732      * @return Boolean
43733      */
43734     isValid : function(){
43735         var valid = true;
43736         this.items.each(function(f){
43737            if(!f.validate()){
43738                valid = false;
43739            }
43740         });
43741         return valid;
43742     },
43743
43744     /**
43745      * Returns true if any fields in this form have changed since their original load.
43746      * @return Boolean
43747      */
43748     isDirty : function(){
43749         var dirty = false;
43750         this.items.each(function(f){
43751            if(f.isDirty()){
43752                dirty = true;
43753                return false;
43754            }
43755         });
43756         return dirty;
43757     },
43758
43759     /**
43760      * Performs a predefined action (submit or load) or custom actions you define on this form.
43761      * @param {String} actionName The name of the action type
43762      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43763      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43764      * accept other config options):
43765      * <pre>
43766 Property          Type             Description
43767 ----------------  ---------------  ----------------------------------------------------------------------------------
43768 url               String           The url for the action (defaults to the form's url)
43769 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43770 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43771 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43772                                    validate the form on the client (defaults to false)
43773      * </pre>
43774      * @return {BasicForm} this
43775      */
43776     doAction : function(action, options){
43777         if(typeof action == 'string'){
43778             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43779         }
43780         if(this.fireEvent('beforeaction', this, action) !== false){
43781             this.beforeAction(action);
43782             action.run.defer(100, action);
43783         }
43784         return this;
43785     },
43786
43787     /**
43788      * Shortcut to do a submit action.
43789      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43790      * @return {BasicForm} this
43791      */
43792     submit : function(options){
43793         this.doAction('submit', options);
43794         return this;
43795     },
43796
43797     /**
43798      * Shortcut to do a load action.
43799      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43800      * @return {BasicForm} this
43801      */
43802     load : function(options){
43803         this.doAction('load', options);
43804         return this;
43805     },
43806
43807     /**
43808      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43809      * @param {Record} record The record to edit
43810      * @return {BasicForm} this
43811      */
43812     updateRecord : function(record){
43813         record.beginEdit();
43814         var fs = record.fields;
43815         fs.each(function(f){
43816             var field = this.findField(f.name);
43817             if(field){
43818                 record.set(f.name, field.getValue());
43819             }
43820         }, this);
43821         record.endEdit();
43822         return this;
43823     },
43824
43825     /**
43826      * Loads an Roo.data.Record into this form.
43827      * @param {Record} record The record to load
43828      * @return {BasicForm} this
43829      */
43830     loadRecord : function(record){
43831         this.setValues(record.data);
43832         return this;
43833     },
43834
43835     // private
43836     beforeAction : function(action){
43837         var o = action.options;
43838         
43839        
43840         if(this.waitMsgTarget === true){
43841             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43842         }else if(this.waitMsgTarget){
43843             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43844             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43845         }else {
43846             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43847         }
43848          
43849     },
43850
43851     // private
43852     afterAction : function(action, success){
43853         this.activeAction = null;
43854         var o = action.options;
43855         
43856         if(this.waitMsgTarget === true){
43857             this.el.unmask();
43858         }else if(this.waitMsgTarget){
43859             this.waitMsgTarget.unmask();
43860         }else{
43861             Roo.MessageBox.updateProgress(1);
43862             Roo.MessageBox.hide();
43863         }
43864          
43865         if(success){
43866             if(o.reset){
43867                 this.reset();
43868             }
43869             Roo.callback(o.success, o.scope, [this, action]);
43870             this.fireEvent('actioncomplete', this, action);
43871             
43872         }else{
43873             
43874             // failure condition..
43875             // we have a scenario where updates need confirming.
43876             // eg. if a locking scenario exists..
43877             // we look for { errors : { needs_confirm : true }} in the response.
43878             if (
43879                 (typeof(action.result) != 'undefined')  &&
43880                 (typeof(action.result.errors) != 'undefined')  &&
43881                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43882            ){
43883                 var _t = this;
43884                 Roo.MessageBox.confirm(
43885                     "Change requires confirmation",
43886                     action.result.errorMsg,
43887                     function(r) {
43888                         if (r != 'yes') {
43889                             return;
43890                         }
43891                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43892                     }
43893                     
43894                 );
43895                 
43896                 
43897                 
43898                 return;
43899             }
43900             
43901             Roo.callback(o.failure, o.scope, [this, action]);
43902             // show an error message if no failed handler is set..
43903             if (!this.hasListener('actionfailed')) {
43904                 Roo.MessageBox.alert("Error",
43905                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43906                         action.result.errorMsg :
43907                         "Saving Failed, please check your entries or try again"
43908                 );
43909             }
43910             
43911             this.fireEvent('actionfailed', this, action);
43912         }
43913         
43914     },
43915
43916     /**
43917      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43918      * @param {String} id The value to search for
43919      * @return Field
43920      */
43921     findField : function(id){
43922         var field = this.items.get(id);
43923         if(!field){
43924             this.items.each(function(f){
43925                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43926                     field = f;
43927                     return false;
43928                 }
43929             });
43930         }
43931         return field || null;
43932     },
43933
43934     /**
43935      * Add a secondary form to this one, 
43936      * Used to provide tabbed forms. One form is primary, with hidden values 
43937      * which mirror the elements from the other forms.
43938      * 
43939      * @param {Roo.form.Form} form to add.
43940      * 
43941      */
43942     addForm : function(form)
43943     {
43944        
43945         if (this.childForms.indexOf(form) > -1) {
43946             // already added..
43947             return;
43948         }
43949         this.childForms.push(form);
43950         var n = '';
43951         Roo.each(form.allItems, function (fe) {
43952             
43953             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43954             if (this.findField(n)) { // already added..
43955                 return;
43956             }
43957             var add = new Roo.form.Hidden({
43958                 name : n
43959             });
43960             add.render(this.el);
43961             
43962             this.add( add );
43963         }, this);
43964         
43965     },
43966     /**
43967      * Mark fields in this form invalid in bulk.
43968      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43969      * @return {BasicForm} this
43970      */
43971     markInvalid : function(errors){
43972         if(errors instanceof Array){
43973             for(var i = 0, len = errors.length; i < len; i++){
43974                 var fieldError = errors[i];
43975                 var f = this.findField(fieldError.id);
43976                 if(f){
43977                     f.markInvalid(fieldError.msg);
43978                 }
43979             }
43980         }else{
43981             var field, id;
43982             for(id in errors){
43983                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43984                     field.markInvalid(errors[id]);
43985                 }
43986             }
43987         }
43988         Roo.each(this.childForms || [], function (f) {
43989             f.markInvalid(errors);
43990         });
43991         
43992         return this;
43993     },
43994
43995     /**
43996      * Set values for fields in this form in bulk.
43997      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43998      * @return {BasicForm} this
43999      */
44000     setValues : function(values){
44001         if(values instanceof Array){ // array of objects
44002             for(var i = 0, len = values.length; i < len; i++){
44003                 var v = values[i];
44004                 var f = this.findField(v.id);
44005                 if(f){
44006                     f.setValue(v.value);
44007                     if(this.trackResetOnLoad){
44008                         f.originalValue = f.getValue();
44009                     }
44010                 }
44011             }
44012         }else{ // object hash
44013             var field, id;
44014             for(id in values){
44015                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44016                     
44017                     if (field.setFromData && 
44018                         field.valueField && 
44019                         field.displayField &&
44020                         // combos' with local stores can 
44021                         // be queried via setValue()
44022                         // to set their value..
44023                         (field.store && !field.store.isLocal)
44024                         ) {
44025                         // it's a combo
44026                         var sd = { };
44027                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44028                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44029                         field.setFromData(sd);
44030                         
44031                     } else {
44032                         field.setValue(values[id]);
44033                     }
44034                     
44035                     
44036                     if(this.trackResetOnLoad){
44037                         field.originalValue = field.getValue();
44038                     }
44039                 }
44040             }
44041         }
44042          
44043         Roo.each(this.childForms || [], function (f) {
44044             f.setValues(values);
44045         });
44046                 
44047         return this;
44048     },
44049
44050     /**
44051      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44052      * they are returned as an array.
44053      * @param {Boolean} asString
44054      * @return {Object}
44055      */
44056     getValues : function(asString){
44057         if (this.childForms) {
44058             // copy values from the child forms
44059             Roo.each(this.childForms, function (f) {
44060                 this.setValues(f.getValues());
44061             }, this);
44062         }
44063         
44064         
44065         
44066         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44067         if(asString === true){
44068             return fs;
44069         }
44070         return Roo.urlDecode(fs);
44071     },
44072     
44073     /**
44074      * Returns the fields in this form as an object with key/value pairs. 
44075      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44076      * @return {Object}
44077      */
44078     getFieldValues : function(with_hidden)
44079     {
44080         if (this.childForms) {
44081             // copy values from the child forms
44082             // should this call getFieldValues - probably not as we do not currently copy
44083             // hidden fields when we generate..
44084             Roo.each(this.childForms, function (f) {
44085                 this.setValues(f.getValues());
44086             }, this);
44087         }
44088         
44089         var ret = {};
44090         this.items.each(function(f){
44091             if (!f.getName()) {
44092                 return;
44093             }
44094             var v = f.getValue();
44095             if (f.inputType =='radio') {
44096                 if (typeof(ret[f.getName()]) == 'undefined') {
44097                     ret[f.getName()] = ''; // empty..
44098                 }
44099                 
44100                 if (!f.el.dom.checked) {
44101                     return;
44102                     
44103                 }
44104                 v = f.el.dom.value;
44105                 
44106             }
44107             
44108             // not sure if this supported any more..
44109             if ((typeof(v) == 'object') && f.getRawValue) {
44110                 v = f.getRawValue() ; // dates..
44111             }
44112             // combo boxes where name != hiddenName...
44113             if (f.name != f.getName()) {
44114                 ret[f.name] = f.getRawValue();
44115             }
44116             ret[f.getName()] = v;
44117         });
44118         
44119         return ret;
44120     },
44121
44122     /**
44123      * Clears all invalid messages in this form.
44124      * @return {BasicForm} this
44125      */
44126     clearInvalid : function(){
44127         this.items.each(function(f){
44128            f.clearInvalid();
44129         });
44130         
44131         Roo.each(this.childForms || [], function (f) {
44132             f.clearInvalid();
44133         });
44134         
44135         
44136         return this;
44137     },
44138
44139     /**
44140      * Resets this form.
44141      * @return {BasicForm} this
44142      */
44143     reset : function(){
44144         this.items.each(function(f){
44145             f.reset();
44146         });
44147         
44148         Roo.each(this.childForms || [], function (f) {
44149             f.reset();
44150         });
44151        
44152         
44153         return this;
44154     },
44155
44156     /**
44157      * Add Roo.form components to this form.
44158      * @param {Field} field1
44159      * @param {Field} field2 (optional)
44160      * @param {Field} etc (optional)
44161      * @return {BasicForm} this
44162      */
44163     add : function(){
44164         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44165         return this;
44166     },
44167
44168
44169     /**
44170      * Removes a field from the items collection (does NOT remove its markup).
44171      * @param {Field} field
44172      * @return {BasicForm} this
44173      */
44174     remove : function(field){
44175         this.items.remove(field);
44176         return this;
44177     },
44178
44179     /**
44180      * Looks at the fields in this form, checks them for an id attribute,
44181      * and calls applyTo on the existing dom element with that id.
44182      * @return {BasicForm} this
44183      */
44184     render : function(){
44185         this.items.each(function(f){
44186             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44187                 f.applyTo(f.id);
44188             }
44189         });
44190         return this;
44191     },
44192
44193     /**
44194      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44195      * @param {Object} values
44196      * @return {BasicForm} this
44197      */
44198     applyToFields : function(o){
44199         this.items.each(function(f){
44200            Roo.apply(f, o);
44201         });
44202         return this;
44203     },
44204
44205     /**
44206      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44207      * @param {Object} values
44208      * @return {BasicForm} this
44209      */
44210     applyIfToFields : function(o){
44211         this.items.each(function(f){
44212            Roo.applyIf(f, o);
44213         });
44214         return this;
44215     }
44216 });
44217
44218 // back compat
44219 Roo.BasicForm = Roo.form.BasicForm;/*
44220  * Based on:
44221  * Ext JS Library 1.1.1
44222  * Copyright(c) 2006-2007, Ext JS, LLC.
44223  *
44224  * Originally Released Under LGPL - original licence link has changed is not relivant.
44225  *
44226  * Fork - LGPL
44227  * <script type="text/javascript">
44228  */
44229
44230 /**
44231  * @class Roo.form.Form
44232  * @extends Roo.form.BasicForm
44233  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44234  * @constructor
44235  * @param {Object} config Configuration options
44236  */
44237 Roo.form.Form = function(config){
44238     var xitems =  [];
44239     if (config.items) {
44240         xitems = config.items;
44241         delete config.items;
44242     }
44243    
44244     
44245     Roo.form.Form.superclass.constructor.call(this, null, config);
44246     this.url = this.url || this.action;
44247     if(!this.root){
44248         this.root = new Roo.form.Layout(Roo.applyIf({
44249             id: Roo.id()
44250         }, config));
44251     }
44252     this.active = this.root;
44253     /**
44254      * Array of all the buttons that have been added to this form via {@link addButton}
44255      * @type Array
44256      */
44257     this.buttons = [];
44258     this.allItems = [];
44259     this.addEvents({
44260         /**
44261          * @event clientvalidation
44262          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44263          * @param {Form} this
44264          * @param {Boolean} valid true if the form has passed client-side validation
44265          */
44266         clientvalidation: true,
44267         /**
44268          * @event rendered
44269          * Fires when the form is rendered
44270          * @param {Roo.form.Form} form
44271          */
44272         rendered : true
44273     });
44274     
44275     if (this.progressUrl) {
44276             // push a hidden field onto the list of fields..
44277             this.addxtype( {
44278                     xns: Roo.form, 
44279                     xtype : 'Hidden', 
44280                     name : 'UPLOAD_IDENTIFIER' 
44281             });
44282         }
44283         
44284     
44285     Roo.each(xitems, this.addxtype, this);
44286     
44287     
44288     
44289 };
44290
44291 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44292     /**
44293      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44294      */
44295     /**
44296      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44297      */
44298     /**
44299      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44300      */
44301     buttonAlign:'center',
44302
44303     /**
44304      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44305      */
44306     minButtonWidth:75,
44307
44308     /**
44309      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44310      * This property cascades to child containers if not set.
44311      */
44312     labelAlign:'left',
44313
44314     /**
44315      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44316      * fires a looping event with that state. This is required to bind buttons to the valid
44317      * state using the config value formBind:true on the button.
44318      */
44319     monitorValid : false,
44320
44321     /**
44322      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44323      */
44324     monitorPoll : 200,
44325     
44326     /**
44327      * @cfg {String} progressUrl - Url to return progress data 
44328      */
44329     
44330     progressUrl : false,
44331   
44332     /**
44333      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44334      * fields are added and the column is closed. If no fields are passed the column remains open
44335      * until end() is called.
44336      * @param {Object} config The config to pass to the column
44337      * @param {Field} field1 (optional)
44338      * @param {Field} field2 (optional)
44339      * @param {Field} etc (optional)
44340      * @return Column The column container object
44341      */
44342     column : function(c){
44343         var col = new Roo.form.Column(c);
44344         this.start(col);
44345         if(arguments.length > 1){ // duplicate code required because of Opera
44346             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44347             this.end();
44348         }
44349         return col;
44350     },
44351
44352     /**
44353      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44354      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44355      * until end() is called.
44356      * @param {Object} config The config to pass to the fieldset
44357      * @param {Field} field1 (optional)
44358      * @param {Field} field2 (optional)
44359      * @param {Field} etc (optional)
44360      * @return FieldSet The fieldset container object
44361      */
44362     fieldset : function(c){
44363         var fs = new Roo.form.FieldSet(c);
44364         this.start(fs);
44365         if(arguments.length > 1){ // duplicate code required because of Opera
44366             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44367             this.end();
44368         }
44369         return fs;
44370     },
44371
44372     /**
44373      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44374      * fields are added and the container is closed. If no fields are passed the container remains open
44375      * until end() is called.
44376      * @param {Object} config The config to pass to the Layout
44377      * @param {Field} field1 (optional)
44378      * @param {Field} field2 (optional)
44379      * @param {Field} etc (optional)
44380      * @return Layout The container object
44381      */
44382     container : function(c){
44383         var l = new Roo.form.Layout(c);
44384         this.start(l);
44385         if(arguments.length > 1){ // duplicate code required because of Opera
44386             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44387             this.end();
44388         }
44389         return l;
44390     },
44391
44392     /**
44393      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44394      * @param {Object} container A Roo.form.Layout or subclass of Layout
44395      * @return {Form} this
44396      */
44397     start : function(c){
44398         // cascade label info
44399         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44400         this.active.stack.push(c);
44401         c.ownerCt = this.active;
44402         this.active = c;
44403         return this;
44404     },
44405
44406     /**
44407      * Closes the current open container
44408      * @return {Form} this
44409      */
44410     end : function(){
44411         if(this.active == this.root){
44412             return this;
44413         }
44414         this.active = this.active.ownerCt;
44415         return this;
44416     },
44417
44418     /**
44419      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44420      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44421      * as the label of the field.
44422      * @param {Field} field1
44423      * @param {Field} field2 (optional)
44424      * @param {Field} etc. (optional)
44425      * @return {Form} this
44426      */
44427     add : function(){
44428         this.active.stack.push.apply(this.active.stack, arguments);
44429         this.allItems.push.apply(this.allItems,arguments);
44430         var r = [];
44431         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44432             if(a[i].isFormField){
44433                 r.push(a[i]);
44434             }
44435         }
44436         if(r.length > 0){
44437             Roo.form.Form.superclass.add.apply(this, r);
44438         }
44439         return this;
44440     },
44441     
44442
44443     
44444     
44445     
44446      /**
44447      * Find any element that has been added to a form, using it's ID or name
44448      * This can include framesets, columns etc. along with regular fields..
44449      * @param {String} id - id or name to find.
44450      
44451      * @return {Element} e - or false if nothing found.
44452      */
44453     findbyId : function(id)
44454     {
44455         var ret = false;
44456         if (!id) {
44457             return ret;
44458         }
44459         Roo.each(this.allItems, function(f){
44460             if (f.id == id || f.name == id ){
44461                 ret = f;
44462                 return false;
44463             }
44464         });
44465         return ret;
44466     },
44467
44468     
44469     
44470     /**
44471      * Render this form into the passed container. This should only be called once!
44472      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44473      * @return {Form} this
44474      */
44475     render : function(ct)
44476     {
44477         
44478         
44479         
44480         ct = Roo.get(ct);
44481         var o = this.autoCreate || {
44482             tag: 'form',
44483             method : this.method || 'POST',
44484             id : this.id || Roo.id()
44485         };
44486         this.initEl(ct.createChild(o));
44487
44488         this.root.render(this.el);
44489         
44490        
44491              
44492         this.items.each(function(f){
44493             f.render('x-form-el-'+f.id);
44494         });
44495
44496         if(this.buttons.length > 0){
44497             // tables are required to maintain order and for correct IE layout
44498             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44499                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44500                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44501             }}, null, true);
44502             var tr = tb.getElementsByTagName('tr')[0];
44503             for(var i = 0, len = this.buttons.length; i < len; i++) {
44504                 var b = this.buttons[i];
44505                 var td = document.createElement('td');
44506                 td.className = 'x-form-btn-td';
44507                 b.render(tr.appendChild(td));
44508             }
44509         }
44510         if(this.monitorValid){ // initialize after render
44511             this.startMonitoring();
44512         }
44513         this.fireEvent('rendered', this);
44514         return this;
44515     },
44516
44517     /**
44518      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44519      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44520      * object or a valid Roo.DomHelper element config
44521      * @param {Function} handler The function called when the button is clicked
44522      * @param {Object} scope (optional) The scope of the handler function
44523      * @return {Roo.Button}
44524      */
44525     addButton : function(config, handler, scope){
44526         var bc = {
44527             handler: handler,
44528             scope: scope,
44529             minWidth: this.minButtonWidth,
44530             hideParent:true
44531         };
44532         if(typeof config == "string"){
44533             bc.text = config;
44534         }else{
44535             Roo.apply(bc, config);
44536         }
44537         var btn = new Roo.Button(null, bc);
44538         this.buttons.push(btn);
44539         return btn;
44540     },
44541
44542      /**
44543      * Adds a series of form elements (using the xtype property as the factory method.
44544      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44545      * @param {Object} config 
44546      */
44547     
44548     addxtype : function()
44549     {
44550         var ar = Array.prototype.slice.call(arguments, 0);
44551         var ret = false;
44552         for(var i = 0; i < ar.length; i++) {
44553             if (!ar[i]) {
44554                 continue; // skip -- if this happends something invalid got sent, we 
44555                 // should ignore it, as basically that interface element will not show up
44556                 // and that should be pretty obvious!!
44557             }
44558             
44559             if (Roo.form[ar[i].xtype]) {
44560                 ar[i].form = this;
44561                 var fe = Roo.factory(ar[i], Roo.form);
44562                 if (!ret) {
44563                     ret = fe;
44564                 }
44565                 fe.form = this;
44566                 if (fe.store) {
44567                     fe.store.form = this;
44568                 }
44569                 if (fe.isLayout) {  
44570                          
44571                     this.start(fe);
44572                     this.allItems.push(fe);
44573                     if (fe.items && fe.addxtype) {
44574                         fe.addxtype.apply(fe, fe.items);
44575                         delete fe.items;
44576                     }
44577                      this.end();
44578                     continue;
44579                 }
44580                 
44581                 
44582                  
44583                 this.add(fe);
44584               //  console.log('adding ' + ar[i].xtype);
44585             }
44586             if (ar[i].xtype == 'Button') {  
44587                 //console.log('adding button');
44588                 //console.log(ar[i]);
44589                 this.addButton(ar[i]);
44590                 this.allItems.push(fe);
44591                 continue;
44592             }
44593             
44594             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44595                 alert('end is not supported on xtype any more, use items');
44596             //    this.end();
44597             //    //console.log('adding end');
44598             }
44599             
44600         }
44601         return ret;
44602     },
44603     
44604     /**
44605      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44606      * option "monitorValid"
44607      */
44608     startMonitoring : function(){
44609         if(!this.bound){
44610             this.bound = true;
44611             Roo.TaskMgr.start({
44612                 run : this.bindHandler,
44613                 interval : this.monitorPoll || 200,
44614                 scope: this
44615             });
44616         }
44617     },
44618
44619     /**
44620      * Stops monitoring of the valid state of this form
44621      */
44622     stopMonitoring : function(){
44623         this.bound = false;
44624     },
44625
44626     // private
44627     bindHandler : function(){
44628         if(!this.bound){
44629             return false; // stops binding
44630         }
44631         var valid = true;
44632         this.items.each(function(f){
44633             if(!f.isValid(true)){
44634                 valid = false;
44635                 return false;
44636             }
44637         });
44638         for(var i = 0, len = this.buttons.length; i < len; i++){
44639             var btn = this.buttons[i];
44640             if(btn.formBind === true && btn.disabled === valid){
44641                 btn.setDisabled(!valid);
44642             }
44643         }
44644         this.fireEvent('clientvalidation', this, valid);
44645     }
44646     
44647     
44648     
44649     
44650     
44651     
44652     
44653     
44654 });
44655
44656
44657 // back compat
44658 Roo.Form = Roo.form.Form;
44659 /*
44660  * Based on:
44661  * Ext JS Library 1.1.1
44662  * Copyright(c) 2006-2007, Ext JS, LLC.
44663  *
44664  * Originally Released Under LGPL - original licence link has changed is not relivant.
44665  *
44666  * Fork - LGPL
44667  * <script type="text/javascript">
44668  */
44669
44670 // as we use this in bootstrap.
44671 Roo.namespace('Roo.form');
44672  /**
44673  * @class Roo.form.Action
44674  * Internal Class used to handle form actions
44675  * @constructor
44676  * @param {Roo.form.BasicForm} el The form element or its id
44677  * @param {Object} config Configuration options
44678  */
44679
44680  
44681  
44682 // define the action interface
44683 Roo.form.Action = function(form, options){
44684     this.form = form;
44685     this.options = options || {};
44686 };
44687 /**
44688  * Client Validation Failed
44689  * @const 
44690  */
44691 Roo.form.Action.CLIENT_INVALID = 'client';
44692 /**
44693  * Server Validation Failed
44694  * @const 
44695  */
44696 Roo.form.Action.SERVER_INVALID = 'server';
44697  /**
44698  * Connect to Server Failed
44699  * @const 
44700  */
44701 Roo.form.Action.CONNECT_FAILURE = 'connect';
44702 /**
44703  * Reading Data from Server Failed
44704  * @const 
44705  */
44706 Roo.form.Action.LOAD_FAILURE = 'load';
44707
44708 Roo.form.Action.prototype = {
44709     type : 'default',
44710     failureType : undefined,
44711     response : undefined,
44712     result : undefined,
44713
44714     // interface method
44715     run : function(options){
44716
44717     },
44718
44719     // interface method
44720     success : function(response){
44721
44722     },
44723
44724     // interface method
44725     handleResponse : function(response){
44726
44727     },
44728
44729     // default connection failure
44730     failure : function(response){
44731         
44732         this.response = response;
44733         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44734         this.form.afterAction(this, false);
44735     },
44736
44737     processResponse : function(response){
44738         this.response = response;
44739         if(!response.responseText){
44740             return true;
44741         }
44742         this.result = this.handleResponse(response);
44743         return this.result;
44744     },
44745
44746     // utility functions used internally
44747     getUrl : function(appendParams){
44748         var url = this.options.url || this.form.url || this.form.el.dom.action;
44749         if(appendParams){
44750             var p = this.getParams();
44751             if(p){
44752                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44753             }
44754         }
44755         return url;
44756     },
44757
44758     getMethod : function(){
44759         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44760     },
44761
44762     getParams : function(){
44763         var bp = this.form.baseParams;
44764         var p = this.options.params;
44765         if(p){
44766             if(typeof p == "object"){
44767                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44768             }else if(typeof p == 'string' && bp){
44769                 p += '&' + Roo.urlEncode(bp);
44770             }
44771         }else if(bp){
44772             p = Roo.urlEncode(bp);
44773         }
44774         return p;
44775     },
44776
44777     createCallback : function(){
44778         return {
44779             success: this.success,
44780             failure: this.failure,
44781             scope: this,
44782             timeout: (this.form.timeout*1000),
44783             upload: this.form.fileUpload ? this.success : undefined
44784         };
44785     }
44786 };
44787
44788 Roo.form.Action.Submit = function(form, options){
44789     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44790 };
44791
44792 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44793     type : 'submit',
44794
44795     haveProgress : false,
44796     uploadComplete : false,
44797     
44798     // uploadProgress indicator.
44799     uploadProgress : function()
44800     {
44801         if (!this.form.progressUrl) {
44802             return;
44803         }
44804         
44805         if (!this.haveProgress) {
44806             Roo.MessageBox.progress("Uploading", "Uploading");
44807         }
44808         if (this.uploadComplete) {
44809            Roo.MessageBox.hide();
44810            return;
44811         }
44812         
44813         this.haveProgress = true;
44814    
44815         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44816         
44817         var c = new Roo.data.Connection();
44818         c.request({
44819             url : this.form.progressUrl,
44820             params: {
44821                 id : uid
44822             },
44823             method: 'GET',
44824             success : function(req){
44825                //console.log(data);
44826                 var rdata = false;
44827                 var edata;
44828                 try  {
44829                    rdata = Roo.decode(req.responseText)
44830                 } catch (e) {
44831                     Roo.log("Invalid data from server..");
44832                     Roo.log(edata);
44833                     return;
44834                 }
44835                 if (!rdata || !rdata.success) {
44836                     Roo.log(rdata);
44837                     Roo.MessageBox.alert(Roo.encode(rdata));
44838                     return;
44839                 }
44840                 var data = rdata.data;
44841                 
44842                 if (this.uploadComplete) {
44843                    Roo.MessageBox.hide();
44844                    return;
44845                 }
44846                    
44847                 if (data){
44848                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44849                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44850                     );
44851                 }
44852                 this.uploadProgress.defer(2000,this);
44853             },
44854        
44855             failure: function(data) {
44856                 Roo.log('progress url failed ');
44857                 Roo.log(data);
44858             },
44859             scope : this
44860         });
44861            
44862     },
44863     
44864     
44865     run : function()
44866     {
44867         // run get Values on the form, so it syncs any secondary forms.
44868         this.form.getValues();
44869         
44870         var o = this.options;
44871         var method = this.getMethod();
44872         var isPost = method == 'POST';
44873         if(o.clientValidation === false || this.form.isValid()){
44874             
44875             if (this.form.progressUrl) {
44876                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44877                     (new Date() * 1) + '' + Math.random());
44878                     
44879             } 
44880             
44881             
44882             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44883                 form:this.form.el.dom,
44884                 url:this.getUrl(!isPost),
44885                 method: method,
44886                 params:isPost ? this.getParams() : null,
44887                 isUpload: this.form.fileUpload
44888             }));
44889             
44890             this.uploadProgress();
44891
44892         }else if (o.clientValidation !== false){ // client validation failed
44893             this.failureType = Roo.form.Action.CLIENT_INVALID;
44894             this.form.afterAction(this, false);
44895         }
44896     },
44897
44898     success : function(response)
44899     {
44900         this.uploadComplete= true;
44901         if (this.haveProgress) {
44902             Roo.MessageBox.hide();
44903         }
44904         
44905         
44906         var result = this.processResponse(response);
44907         if(result === true || result.success){
44908             this.form.afterAction(this, true);
44909             return;
44910         }
44911         if(result.errors){
44912             this.form.markInvalid(result.errors);
44913             this.failureType = Roo.form.Action.SERVER_INVALID;
44914         }
44915         this.form.afterAction(this, false);
44916     },
44917     failure : function(response)
44918     {
44919         this.uploadComplete= true;
44920         if (this.haveProgress) {
44921             Roo.MessageBox.hide();
44922         }
44923         
44924         this.response = response;
44925         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44926         this.form.afterAction(this, false);
44927     },
44928     
44929     handleResponse : function(response){
44930         if(this.form.errorReader){
44931             var rs = this.form.errorReader.read(response);
44932             var errors = [];
44933             if(rs.records){
44934                 for(var i = 0, len = rs.records.length; i < len; i++) {
44935                     var r = rs.records[i];
44936                     errors[i] = r.data;
44937                 }
44938             }
44939             if(errors.length < 1){
44940                 errors = null;
44941             }
44942             return {
44943                 success : rs.success,
44944                 errors : errors
44945             };
44946         }
44947         var ret = false;
44948         try {
44949             ret = Roo.decode(response.responseText);
44950         } catch (e) {
44951             ret = {
44952                 success: false,
44953                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44954                 errors : []
44955             };
44956         }
44957         return ret;
44958         
44959     }
44960 });
44961
44962
44963 Roo.form.Action.Load = function(form, options){
44964     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44965     this.reader = this.form.reader;
44966 };
44967
44968 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44969     type : 'load',
44970
44971     run : function(){
44972         
44973         Roo.Ajax.request(Roo.apply(
44974                 this.createCallback(), {
44975                     method:this.getMethod(),
44976                     url:this.getUrl(false),
44977                     params:this.getParams()
44978         }));
44979     },
44980
44981     success : function(response){
44982         
44983         var result = this.processResponse(response);
44984         if(result === true || !result.success || !result.data){
44985             this.failureType = Roo.form.Action.LOAD_FAILURE;
44986             this.form.afterAction(this, false);
44987             return;
44988         }
44989         this.form.clearInvalid();
44990         this.form.setValues(result.data);
44991         this.form.afterAction(this, true);
44992     },
44993
44994     handleResponse : function(response){
44995         if(this.form.reader){
44996             var rs = this.form.reader.read(response);
44997             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44998             return {
44999                 success : rs.success,
45000                 data : data
45001             };
45002         }
45003         return Roo.decode(response.responseText);
45004     }
45005 });
45006
45007 Roo.form.Action.ACTION_TYPES = {
45008     'load' : Roo.form.Action.Load,
45009     'submit' : Roo.form.Action.Submit
45010 };/*
45011  * Based on:
45012  * Ext JS Library 1.1.1
45013  * Copyright(c) 2006-2007, Ext JS, LLC.
45014  *
45015  * Originally Released Under LGPL - original licence link has changed is not relivant.
45016  *
45017  * Fork - LGPL
45018  * <script type="text/javascript">
45019  */
45020  
45021 /**
45022  * @class Roo.form.Layout
45023  * @extends Roo.Component
45024  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45025  * @constructor
45026  * @param {Object} config Configuration options
45027  */
45028 Roo.form.Layout = function(config){
45029     var xitems = [];
45030     if (config.items) {
45031         xitems = config.items;
45032         delete config.items;
45033     }
45034     Roo.form.Layout.superclass.constructor.call(this, config);
45035     this.stack = [];
45036     Roo.each(xitems, this.addxtype, this);
45037      
45038 };
45039
45040 Roo.extend(Roo.form.Layout, Roo.Component, {
45041     /**
45042      * @cfg {String/Object} autoCreate
45043      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45044      */
45045     /**
45046      * @cfg {String/Object/Function} style
45047      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45048      * a function which returns such a specification.
45049      */
45050     /**
45051      * @cfg {String} labelAlign
45052      * Valid values are "left," "top" and "right" (defaults to "left")
45053      */
45054     /**
45055      * @cfg {Number} labelWidth
45056      * Fixed width in pixels of all field labels (defaults to undefined)
45057      */
45058     /**
45059      * @cfg {Boolean} clear
45060      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45061      */
45062     clear : true,
45063     /**
45064      * @cfg {String} labelSeparator
45065      * The separator to use after field labels (defaults to ':')
45066      */
45067     labelSeparator : ':',
45068     /**
45069      * @cfg {Boolean} hideLabels
45070      * True to suppress the display of field labels in this layout (defaults to false)
45071      */
45072     hideLabels : false,
45073
45074     // private
45075     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45076     
45077     isLayout : true,
45078     
45079     // private
45080     onRender : function(ct, position){
45081         if(this.el){ // from markup
45082             this.el = Roo.get(this.el);
45083         }else {  // generate
45084             var cfg = this.getAutoCreate();
45085             this.el = ct.createChild(cfg, position);
45086         }
45087         if(this.style){
45088             this.el.applyStyles(this.style);
45089         }
45090         if(this.labelAlign){
45091             this.el.addClass('x-form-label-'+this.labelAlign);
45092         }
45093         if(this.hideLabels){
45094             this.labelStyle = "display:none";
45095             this.elementStyle = "padding-left:0;";
45096         }else{
45097             if(typeof this.labelWidth == 'number'){
45098                 this.labelStyle = "width:"+this.labelWidth+"px;";
45099                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45100             }
45101             if(this.labelAlign == 'top'){
45102                 this.labelStyle = "width:auto;";
45103                 this.elementStyle = "padding-left:0;";
45104             }
45105         }
45106         var stack = this.stack;
45107         var slen = stack.length;
45108         if(slen > 0){
45109             if(!this.fieldTpl){
45110                 var t = new Roo.Template(
45111                     '<div class="x-form-item {5}">',
45112                         '<label for="{0}" style="{2}">{1}{4}</label>',
45113                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45114                         '</div>',
45115                     '</div><div class="x-form-clear-left"></div>'
45116                 );
45117                 t.disableFormats = true;
45118                 t.compile();
45119                 Roo.form.Layout.prototype.fieldTpl = t;
45120             }
45121             for(var i = 0; i < slen; i++) {
45122                 if(stack[i].isFormField){
45123                     this.renderField(stack[i]);
45124                 }else{
45125                     this.renderComponent(stack[i]);
45126                 }
45127             }
45128         }
45129         if(this.clear){
45130             this.el.createChild({cls:'x-form-clear'});
45131         }
45132     },
45133
45134     // private
45135     renderField : function(f){
45136         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45137                f.id, //0
45138                f.fieldLabel, //1
45139                f.labelStyle||this.labelStyle||'', //2
45140                this.elementStyle||'', //3
45141                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45142                f.itemCls||this.itemCls||''  //5
45143        ], true).getPrevSibling());
45144     },
45145
45146     // private
45147     renderComponent : function(c){
45148         c.render(c.isLayout ? this.el : this.el.createChild());    
45149     },
45150     /**
45151      * Adds a object form elements (using the xtype property as the factory method.)
45152      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45153      * @param {Object} config 
45154      */
45155     addxtype : function(o)
45156     {
45157         // create the lement.
45158         o.form = this.form;
45159         var fe = Roo.factory(o, Roo.form);
45160         this.form.allItems.push(fe);
45161         this.stack.push(fe);
45162         
45163         if (fe.isFormField) {
45164             this.form.items.add(fe);
45165         }
45166          
45167         return fe;
45168     }
45169 });
45170
45171 /**
45172  * @class Roo.form.Column
45173  * @extends Roo.form.Layout
45174  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45175  * @constructor
45176  * @param {Object} config Configuration options
45177  */
45178 Roo.form.Column = function(config){
45179     Roo.form.Column.superclass.constructor.call(this, config);
45180 };
45181
45182 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45183     /**
45184      * @cfg {Number/String} width
45185      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45186      */
45187     /**
45188      * @cfg {String/Object} autoCreate
45189      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45190      */
45191
45192     // private
45193     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45194
45195     // private
45196     onRender : function(ct, position){
45197         Roo.form.Column.superclass.onRender.call(this, ct, position);
45198         if(this.width){
45199             this.el.setWidth(this.width);
45200         }
45201     }
45202 });
45203
45204
45205 /**
45206  * @class Roo.form.Row
45207  * @extends Roo.form.Layout
45208  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45209  * @constructor
45210  * @param {Object} config Configuration options
45211  */
45212
45213  
45214 Roo.form.Row = function(config){
45215     Roo.form.Row.superclass.constructor.call(this, config);
45216 };
45217  
45218 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45219       /**
45220      * @cfg {Number/String} width
45221      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45222      */
45223     /**
45224      * @cfg {Number/String} height
45225      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45226      */
45227     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45228     
45229     padWidth : 20,
45230     // private
45231     onRender : function(ct, position){
45232         //console.log('row render');
45233         if(!this.rowTpl){
45234             var t = new Roo.Template(
45235                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45236                     '<label for="{0}" style="{2}">{1}{4}</label>',
45237                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45238                     '</div>',
45239                 '</div>'
45240             );
45241             t.disableFormats = true;
45242             t.compile();
45243             Roo.form.Layout.prototype.rowTpl = t;
45244         }
45245         this.fieldTpl = this.rowTpl;
45246         
45247         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45248         var labelWidth = 100;
45249         
45250         if ((this.labelAlign != 'top')) {
45251             if (typeof this.labelWidth == 'number') {
45252                 labelWidth = this.labelWidth
45253             }
45254             this.padWidth =  20 + labelWidth;
45255             
45256         }
45257         
45258         Roo.form.Column.superclass.onRender.call(this, ct, position);
45259         if(this.width){
45260             this.el.setWidth(this.width);
45261         }
45262         if(this.height){
45263             this.el.setHeight(this.height);
45264         }
45265     },
45266     
45267     // private
45268     renderField : function(f){
45269         f.fieldEl = this.fieldTpl.append(this.el, [
45270                f.id, f.fieldLabel,
45271                f.labelStyle||this.labelStyle||'',
45272                this.elementStyle||'',
45273                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45274                f.itemCls||this.itemCls||'',
45275                f.width ? f.width + this.padWidth : 160 + this.padWidth
45276        ],true);
45277     }
45278 });
45279  
45280
45281 /**
45282  * @class Roo.form.FieldSet
45283  * @extends Roo.form.Layout
45284  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45285  * @constructor
45286  * @param {Object} config Configuration options
45287  */
45288 Roo.form.FieldSet = function(config){
45289     Roo.form.FieldSet.superclass.constructor.call(this, config);
45290 };
45291
45292 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45293     /**
45294      * @cfg {String} legend
45295      * The text to display as the legend for the FieldSet (defaults to '')
45296      */
45297     /**
45298      * @cfg {String/Object} autoCreate
45299      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45300      */
45301
45302     // private
45303     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45304
45305     // private
45306     onRender : function(ct, position){
45307         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45308         if(this.legend){
45309             this.setLegend(this.legend);
45310         }
45311     },
45312
45313     // private
45314     setLegend : function(text){
45315         if(this.rendered){
45316             this.el.child('legend').update(text);
45317         }
45318     }
45319 });/*
45320  * Based on:
45321  * Ext JS Library 1.1.1
45322  * Copyright(c) 2006-2007, Ext JS, LLC.
45323  *
45324  * Originally Released Under LGPL - original licence link has changed is not relivant.
45325  *
45326  * Fork - LGPL
45327  * <script type="text/javascript">
45328  */
45329 /**
45330  * @class Roo.form.VTypes
45331  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45332  * @singleton
45333  */
45334 Roo.form.VTypes = function(){
45335     // closure these in so they are only created once.
45336     var alpha = /^[a-zA-Z_]+$/;
45337     var alphanum = /^[a-zA-Z0-9_]+$/;
45338     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45339     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45340
45341     // All these messages and functions are configurable
45342     return {
45343         /**
45344          * The function used to validate email addresses
45345          * @param {String} value The email address
45346          */
45347         'email' : function(v){
45348             return email.test(v);
45349         },
45350         /**
45351          * The error text to display when the email validation function returns false
45352          * @type String
45353          */
45354         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45355         /**
45356          * The keystroke filter mask to be applied on email input
45357          * @type RegExp
45358          */
45359         'emailMask' : /[a-z0-9_\.\-@]/i,
45360
45361         /**
45362          * The function used to validate URLs
45363          * @param {String} value The URL
45364          */
45365         'url' : function(v){
45366             return url.test(v);
45367         },
45368         /**
45369          * The error text to display when the url validation function returns false
45370          * @type String
45371          */
45372         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45373         
45374         /**
45375          * The function used to validate alpha values
45376          * @param {String} value The value
45377          */
45378         'alpha' : function(v){
45379             return alpha.test(v);
45380         },
45381         /**
45382          * The error text to display when the alpha validation function returns false
45383          * @type String
45384          */
45385         'alphaText' : 'This field should only contain letters and _',
45386         /**
45387          * The keystroke filter mask to be applied on alpha input
45388          * @type RegExp
45389          */
45390         'alphaMask' : /[a-z_]/i,
45391
45392         /**
45393          * The function used to validate alphanumeric values
45394          * @param {String} value The value
45395          */
45396         'alphanum' : function(v){
45397             return alphanum.test(v);
45398         },
45399         /**
45400          * The error text to display when the alphanumeric validation function returns false
45401          * @type String
45402          */
45403         'alphanumText' : 'This field should only contain letters, numbers and _',
45404         /**
45405          * The keystroke filter mask to be applied on alphanumeric input
45406          * @type RegExp
45407          */
45408         'alphanumMask' : /[a-z0-9_]/i
45409     };
45410 }();//<script type="text/javascript">
45411
45412 /**
45413  * @class Roo.form.FCKeditor
45414  * @extends Roo.form.TextArea
45415  * Wrapper around the FCKEditor http://www.fckeditor.net
45416  * @constructor
45417  * Creates a new FCKeditor
45418  * @param {Object} config Configuration options
45419  */
45420 Roo.form.FCKeditor = function(config){
45421     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45422     this.addEvents({
45423          /**
45424          * @event editorinit
45425          * Fired when the editor is initialized - you can add extra handlers here..
45426          * @param {FCKeditor} this
45427          * @param {Object} the FCK object.
45428          */
45429         editorinit : true
45430     });
45431     
45432     
45433 };
45434 Roo.form.FCKeditor.editors = { };
45435 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45436 {
45437     //defaultAutoCreate : {
45438     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45439     //},
45440     // private
45441     /**
45442      * @cfg {Object} fck options - see fck manual for details.
45443      */
45444     fckconfig : false,
45445     
45446     /**
45447      * @cfg {Object} fck toolbar set (Basic or Default)
45448      */
45449     toolbarSet : 'Basic',
45450     /**
45451      * @cfg {Object} fck BasePath
45452      */ 
45453     basePath : '/fckeditor/',
45454     
45455     
45456     frame : false,
45457     
45458     value : '',
45459     
45460    
45461     onRender : function(ct, position)
45462     {
45463         if(!this.el){
45464             this.defaultAutoCreate = {
45465                 tag: "textarea",
45466                 style:"width:300px;height:60px;",
45467                 autocomplete: "off"
45468             };
45469         }
45470         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45471         /*
45472         if(this.grow){
45473             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45474             if(this.preventScrollbars){
45475                 this.el.setStyle("overflow", "hidden");
45476             }
45477             this.el.setHeight(this.growMin);
45478         }
45479         */
45480         //console.log('onrender' + this.getId() );
45481         Roo.form.FCKeditor.editors[this.getId()] = this;
45482          
45483
45484         this.replaceTextarea() ;
45485         
45486     },
45487     
45488     getEditor : function() {
45489         return this.fckEditor;
45490     },
45491     /**
45492      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45493      * @param {Mixed} value The value to set
45494      */
45495     
45496     
45497     setValue : function(value)
45498     {
45499         //console.log('setValue: ' + value);
45500         
45501         if(typeof(value) == 'undefined') { // not sure why this is happending...
45502             return;
45503         }
45504         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45505         
45506         //if(!this.el || !this.getEditor()) {
45507         //    this.value = value;
45508             //this.setValue.defer(100,this,[value]);    
45509         //    return;
45510         //} 
45511         
45512         if(!this.getEditor()) {
45513             return;
45514         }
45515         
45516         this.getEditor().SetData(value);
45517         
45518         //
45519
45520     },
45521
45522     /**
45523      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45524      * @return {Mixed} value The field value
45525      */
45526     getValue : function()
45527     {
45528         
45529         if (this.frame && this.frame.dom.style.display == 'none') {
45530             return Roo.form.FCKeditor.superclass.getValue.call(this);
45531         }
45532         
45533         if(!this.el || !this.getEditor()) {
45534            
45535            // this.getValue.defer(100,this); 
45536             return this.value;
45537         }
45538        
45539         
45540         var value=this.getEditor().GetData();
45541         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45542         return Roo.form.FCKeditor.superclass.getValue.call(this);
45543         
45544
45545     },
45546
45547     /**
45548      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45549      * @return {Mixed} value The field value
45550      */
45551     getRawValue : function()
45552     {
45553         if (this.frame && this.frame.dom.style.display == 'none') {
45554             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45555         }
45556         
45557         if(!this.el || !this.getEditor()) {
45558             //this.getRawValue.defer(100,this); 
45559             return this.value;
45560             return;
45561         }
45562         
45563         
45564         
45565         var value=this.getEditor().GetData();
45566         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45567         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45568          
45569     },
45570     
45571     setSize : function(w,h) {
45572         
45573         
45574         
45575         //if (this.frame && this.frame.dom.style.display == 'none') {
45576         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45577         //    return;
45578         //}
45579         //if(!this.el || !this.getEditor()) {
45580         //    this.setSize.defer(100,this, [w,h]); 
45581         //    return;
45582         //}
45583         
45584         
45585         
45586         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45587         
45588         this.frame.dom.setAttribute('width', w);
45589         this.frame.dom.setAttribute('height', h);
45590         this.frame.setSize(w,h);
45591         
45592     },
45593     
45594     toggleSourceEdit : function(value) {
45595         
45596       
45597          
45598         this.el.dom.style.display = value ? '' : 'none';
45599         this.frame.dom.style.display = value ?  'none' : '';
45600         
45601     },
45602     
45603     
45604     focus: function(tag)
45605     {
45606         if (this.frame.dom.style.display == 'none') {
45607             return Roo.form.FCKeditor.superclass.focus.call(this);
45608         }
45609         if(!this.el || !this.getEditor()) {
45610             this.focus.defer(100,this, [tag]); 
45611             return;
45612         }
45613         
45614         
45615         
45616         
45617         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45618         this.getEditor().Focus();
45619         if (tgs.length) {
45620             if (!this.getEditor().Selection.GetSelection()) {
45621                 this.focus.defer(100,this, [tag]); 
45622                 return;
45623             }
45624             
45625             
45626             var r = this.getEditor().EditorDocument.createRange();
45627             r.setStart(tgs[0],0);
45628             r.setEnd(tgs[0],0);
45629             this.getEditor().Selection.GetSelection().removeAllRanges();
45630             this.getEditor().Selection.GetSelection().addRange(r);
45631             this.getEditor().Focus();
45632         }
45633         
45634     },
45635     
45636     
45637     
45638     replaceTextarea : function()
45639     {
45640         if ( document.getElementById( this.getId() + '___Frame' ) )
45641             return ;
45642         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45643         //{
45644             // We must check the elements firstly using the Id and then the name.
45645         var oTextarea = document.getElementById( this.getId() );
45646         
45647         var colElementsByName = document.getElementsByName( this.getId() ) ;
45648          
45649         oTextarea.style.display = 'none' ;
45650
45651         if ( oTextarea.tabIndex ) {            
45652             this.TabIndex = oTextarea.tabIndex ;
45653         }
45654         
45655         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45656         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45657         this.frame = Roo.get(this.getId() + '___Frame')
45658     },
45659     
45660     _getConfigHtml : function()
45661     {
45662         var sConfig = '' ;
45663
45664         for ( var o in this.fckconfig ) {
45665             sConfig += sConfig.length > 0  ? '&amp;' : '';
45666             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45667         }
45668
45669         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45670     },
45671     
45672     
45673     _getIFrameHtml : function()
45674     {
45675         var sFile = 'fckeditor.html' ;
45676         /* no idea what this is about..
45677         try
45678         {
45679             if ( (/fcksource=true/i).test( window.top.location.search ) )
45680                 sFile = 'fckeditor.original.html' ;
45681         }
45682         catch (e) { 
45683         */
45684
45685         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45686         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45687         
45688         
45689         var html = '<iframe id="' + this.getId() +
45690             '___Frame" src="' + sLink +
45691             '" width="' + this.width +
45692             '" height="' + this.height + '"' +
45693             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45694             ' frameborder="0" scrolling="no"></iframe>' ;
45695
45696         return html ;
45697     },
45698     
45699     _insertHtmlBefore : function( html, element )
45700     {
45701         if ( element.insertAdjacentHTML )       {
45702             // IE
45703             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45704         } else { // Gecko
45705             var oRange = document.createRange() ;
45706             oRange.setStartBefore( element ) ;
45707             var oFragment = oRange.createContextualFragment( html );
45708             element.parentNode.insertBefore( oFragment, element ) ;
45709         }
45710     }
45711     
45712     
45713   
45714     
45715     
45716     
45717     
45718
45719 });
45720
45721 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45722
45723 function FCKeditor_OnComplete(editorInstance){
45724     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45725     f.fckEditor = editorInstance;
45726     //console.log("loaded");
45727     f.fireEvent('editorinit', f, editorInstance);
45728
45729   
45730
45731  
45732
45733
45734
45735
45736
45737
45738
45739
45740
45741
45742
45743
45744
45745
45746
45747 //<script type="text/javascript">
45748 /**
45749  * @class Roo.form.GridField
45750  * @extends Roo.form.Field
45751  * Embed a grid (or editable grid into a form)
45752  * STATUS ALPHA
45753  * 
45754  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45755  * it needs 
45756  * xgrid.store = Roo.data.Store
45757  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45758  * xgrid.store.reader = Roo.data.JsonReader 
45759  * 
45760  * 
45761  * @constructor
45762  * Creates a new GridField
45763  * @param {Object} config Configuration options
45764  */
45765 Roo.form.GridField = function(config){
45766     Roo.form.GridField.superclass.constructor.call(this, config);
45767      
45768 };
45769
45770 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45771     /**
45772      * @cfg {Number} width  - used to restrict width of grid..
45773      */
45774     width : 100,
45775     /**
45776      * @cfg {Number} height - used to restrict height of grid..
45777      */
45778     height : 50,
45779      /**
45780      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45781          * 
45782          *}
45783      */
45784     xgrid : false, 
45785     /**
45786      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45787      * {tag: "input", type: "checkbox", autocomplete: "off"})
45788      */
45789    // defaultAutoCreate : { tag: 'div' },
45790     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45791     /**
45792      * @cfg {String} addTitle Text to include for adding a title.
45793      */
45794     addTitle : false,
45795     //
45796     onResize : function(){
45797         Roo.form.Field.superclass.onResize.apply(this, arguments);
45798     },
45799
45800     initEvents : function(){
45801         // Roo.form.Checkbox.superclass.initEvents.call(this);
45802         // has no events...
45803        
45804     },
45805
45806
45807     getResizeEl : function(){
45808         return this.wrap;
45809     },
45810
45811     getPositionEl : function(){
45812         return this.wrap;
45813     },
45814
45815     // private
45816     onRender : function(ct, position){
45817         
45818         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45819         var style = this.style;
45820         delete this.style;
45821         
45822         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45823         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45824         this.viewEl = this.wrap.createChild({ tag: 'div' });
45825         if (style) {
45826             this.viewEl.applyStyles(style);
45827         }
45828         if (this.width) {
45829             this.viewEl.setWidth(this.width);
45830         }
45831         if (this.height) {
45832             this.viewEl.setHeight(this.height);
45833         }
45834         //if(this.inputValue !== undefined){
45835         //this.setValue(this.value);
45836         
45837         
45838         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45839         
45840         
45841         this.grid.render();
45842         this.grid.getDataSource().on('remove', this.refreshValue, this);
45843         this.grid.getDataSource().on('update', this.refreshValue, this);
45844         this.grid.on('afteredit', this.refreshValue, this);
45845  
45846     },
45847      
45848     
45849     /**
45850      * Sets the value of the item. 
45851      * @param {String} either an object  or a string..
45852      */
45853     setValue : function(v){
45854         //this.value = v;
45855         v = v || []; // empty set..
45856         // this does not seem smart - it really only affects memoryproxy grids..
45857         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45858             var ds = this.grid.getDataSource();
45859             // assumes a json reader..
45860             var data = {}
45861             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45862             ds.loadData( data);
45863         }
45864         // clear selection so it does not get stale.
45865         if (this.grid.sm) { 
45866             this.grid.sm.clearSelections();
45867         }
45868         
45869         Roo.form.GridField.superclass.setValue.call(this, v);
45870         this.refreshValue();
45871         // should load data in the grid really....
45872     },
45873     
45874     // private
45875     refreshValue: function() {
45876          var val = [];
45877         this.grid.getDataSource().each(function(r) {
45878             val.push(r.data);
45879         });
45880         this.el.dom.value = Roo.encode(val);
45881     }
45882     
45883      
45884     
45885     
45886 });/*
45887  * Based on:
45888  * Ext JS Library 1.1.1
45889  * Copyright(c) 2006-2007, Ext JS, LLC.
45890  *
45891  * Originally Released Under LGPL - original licence link has changed is not relivant.
45892  *
45893  * Fork - LGPL
45894  * <script type="text/javascript">
45895  */
45896 /**
45897  * @class Roo.form.DisplayField
45898  * @extends Roo.form.Field
45899  * A generic Field to display non-editable data.
45900  * @constructor
45901  * Creates a new Display Field item.
45902  * @param {Object} config Configuration options
45903  */
45904 Roo.form.DisplayField = function(config){
45905     Roo.form.DisplayField.superclass.constructor.call(this, config);
45906     
45907 };
45908
45909 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45910     inputType:      'hidden',
45911     allowBlank:     true,
45912     readOnly:         true,
45913     
45914  
45915     /**
45916      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45917      */
45918     focusClass : undefined,
45919     /**
45920      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45921      */
45922     fieldClass: 'x-form-field',
45923     
45924      /**
45925      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45926      */
45927     valueRenderer: undefined,
45928     
45929     width: 100,
45930     /**
45931      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45932      * {tag: "input", type: "checkbox", autocomplete: "off"})
45933      */
45934      
45935  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45936
45937     onResize : function(){
45938         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45939         
45940     },
45941
45942     initEvents : function(){
45943         // Roo.form.Checkbox.superclass.initEvents.call(this);
45944         // has no events...
45945        
45946     },
45947
45948
45949     getResizeEl : function(){
45950         return this.wrap;
45951     },
45952
45953     getPositionEl : function(){
45954         return this.wrap;
45955     },
45956
45957     // private
45958     onRender : function(ct, position){
45959         
45960         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45961         //if(this.inputValue !== undefined){
45962         this.wrap = this.el.wrap();
45963         
45964         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45965         
45966         if (this.bodyStyle) {
45967             this.viewEl.applyStyles(this.bodyStyle);
45968         }
45969         //this.viewEl.setStyle('padding', '2px');
45970         
45971         this.setValue(this.value);
45972         
45973     },
45974 /*
45975     // private
45976     initValue : Roo.emptyFn,
45977
45978   */
45979
45980         // private
45981     onClick : function(){
45982         
45983     },
45984
45985     /**
45986      * Sets the checked state of the checkbox.
45987      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45988      */
45989     setValue : function(v){
45990         this.value = v;
45991         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45992         // this might be called before we have a dom element..
45993         if (!this.viewEl) {
45994             return;
45995         }
45996         this.viewEl.dom.innerHTML = html;
45997         Roo.form.DisplayField.superclass.setValue.call(this, v);
45998
45999     }
46000 });/*
46001  * 
46002  * Licence- LGPL
46003  * 
46004  */
46005
46006 /**
46007  * @class Roo.form.DayPicker
46008  * @extends Roo.form.Field
46009  * A Day picker show [M] [T] [W] ....
46010  * @constructor
46011  * Creates a new Day Picker
46012  * @param {Object} config Configuration options
46013  */
46014 Roo.form.DayPicker= function(config){
46015     Roo.form.DayPicker.superclass.constructor.call(this, config);
46016      
46017 };
46018
46019 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46020     /**
46021      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46022      */
46023     focusClass : undefined,
46024     /**
46025      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46026      */
46027     fieldClass: "x-form-field",
46028    
46029     /**
46030      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46031      * {tag: "input", type: "checkbox", autocomplete: "off"})
46032      */
46033     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46034     
46035    
46036     actionMode : 'viewEl', 
46037     //
46038     // private
46039  
46040     inputType : 'hidden',
46041     
46042      
46043     inputElement: false, // real input element?
46044     basedOn: false, // ????
46045     
46046     isFormField: true, // not sure where this is needed!!!!
46047
46048     onResize : function(){
46049         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46050         if(!this.boxLabel){
46051             this.el.alignTo(this.wrap, 'c-c');
46052         }
46053     },
46054
46055     initEvents : function(){
46056         Roo.form.Checkbox.superclass.initEvents.call(this);
46057         this.el.on("click", this.onClick,  this);
46058         this.el.on("change", this.onClick,  this);
46059     },
46060
46061
46062     getResizeEl : function(){
46063         return this.wrap;
46064     },
46065
46066     getPositionEl : function(){
46067         return this.wrap;
46068     },
46069
46070     
46071     // private
46072     onRender : function(ct, position){
46073         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46074        
46075         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46076         
46077         var r1 = '<table><tr>';
46078         var r2 = '<tr class="x-form-daypick-icons">';
46079         for (var i=0; i < 7; i++) {
46080             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46081             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46082         }
46083         
46084         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46085         viewEl.select('img').on('click', this.onClick, this);
46086         this.viewEl = viewEl;   
46087         
46088         
46089         // this will not work on Chrome!!!
46090         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46091         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46092         
46093         
46094           
46095
46096     },
46097
46098     // private
46099     initValue : Roo.emptyFn,
46100
46101     /**
46102      * Returns the checked state of the checkbox.
46103      * @return {Boolean} True if checked, else false
46104      */
46105     getValue : function(){
46106         return this.el.dom.value;
46107         
46108     },
46109
46110         // private
46111     onClick : function(e){ 
46112         //this.setChecked(!this.checked);
46113         Roo.get(e.target).toggleClass('x-menu-item-checked');
46114         this.refreshValue();
46115         //if(this.el.dom.checked != this.checked){
46116         //    this.setValue(this.el.dom.checked);
46117        // }
46118     },
46119     
46120     // private
46121     refreshValue : function()
46122     {
46123         var val = '';
46124         this.viewEl.select('img',true).each(function(e,i,n)  {
46125             val += e.is(".x-menu-item-checked") ? String(n) : '';
46126         });
46127         this.setValue(val, true);
46128     },
46129
46130     /**
46131      * Sets the checked state of the checkbox.
46132      * On is always based on a string comparison between inputValue and the param.
46133      * @param {Boolean/String} value - the value to set 
46134      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46135      */
46136     setValue : function(v,suppressEvent){
46137         if (!this.el.dom) {
46138             return;
46139         }
46140         var old = this.el.dom.value ;
46141         this.el.dom.value = v;
46142         if (suppressEvent) {
46143             return ;
46144         }
46145          
46146         // update display..
46147         this.viewEl.select('img',true).each(function(e,i,n)  {
46148             
46149             var on = e.is(".x-menu-item-checked");
46150             var newv = v.indexOf(String(n)) > -1;
46151             if (on != newv) {
46152                 e.toggleClass('x-menu-item-checked');
46153             }
46154             
46155         });
46156         
46157         
46158         this.fireEvent('change', this, v, old);
46159         
46160         
46161     },
46162    
46163     // handle setting of hidden value by some other method!!?!?
46164     setFromHidden: function()
46165     {
46166         if(!this.el){
46167             return;
46168         }
46169         //console.log("SET FROM HIDDEN");
46170         //alert('setFrom hidden');
46171         this.setValue(this.el.dom.value);
46172     },
46173     
46174     onDestroy : function()
46175     {
46176         if(this.viewEl){
46177             Roo.get(this.viewEl).remove();
46178         }
46179          
46180         Roo.form.DayPicker.superclass.onDestroy.call(this);
46181     }
46182
46183 });/*
46184  * RooJS Library 1.1.1
46185  * Copyright(c) 2008-2011  Alan Knowles
46186  *
46187  * License - LGPL
46188  */
46189  
46190
46191 /**
46192  * @class Roo.form.ComboCheck
46193  * @extends Roo.form.ComboBox
46194  * A combobox for multiple select items.
46195  *
46196  * FIXME - could do with a reset button..
46197  * 
46198  * @constructor
46199  * Create a new ComboCheck
46200  * @param {Object} config Configuration options
46201  */
46202 Roo.form.ComboCheck = function(config){
46203     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46204     // should verify some data...
46205     // like
46206     // hiddenName = required..
46207     // displayField = required
46208     // valudField == required
46209     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46210     var _t = this;
46211     Roo.each(req, function(e) {
46212         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46213             throw "Roo.form.ComboCheck : missing value for: " + e;
46214         }
46215     });
46216     
46217     
46218 };
46219
46220 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46221      
46222      
46223     editable : false,
46224      
46225     selectedClass: 'x-menu-item-checked', 
46226     
46227     // private
46228     onRender : function(ct, position){
46229         var _t = this;
46230         
46231         
46232         
46233         if(!this.tpl){
46234             var cls = 'x-combo-list';
46235
46236             
46237             this.tpl =  new Roo.Template({
46238                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46239                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46240                    '<span>{' + this.displayField + '}</span>' +
46241                     '</div>' 
46242                 
46243             });
46244         }
46245  
46246         
46247         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46248         this.view.singleSelect = false;
46249         this.view.multiSelect = true;
46250         this.view.toggleSelect = true;
46251         this.pageTb.add(new Roo.Toolbar.Fill(), {
46252             
46253             text: 'Done',
46254             handler: function()
46255             {
46256                 _t.collapse();
46257             }
46258         });
46259     },
46260     
46261     onViewOver : function(e, t){
46262         // do nothing...
46263         return;
46264         
46265     },
46266     
46267     onViewClick : function(doFocus,index){
46268         return;
46269         
46270     },
46271     select: function () {
46272         //Roo.log("SELECT CALLED");
46273     },
46274      
46275     selectByValue : function(xv, scrollIntoView){
46276         var ar = this.getValueArray();
46277         var sels = [];
46278         
46279         Roo.each(ar, function(v) {
46280             if(v === undefined || v === null){
46281                 return;
46282             }
46283             var r = this.findRecord(this.valueField, v);
46284             if(r){
46285                 sels.push(this.store.indexOf(r))
46286                 
46287             }
46288         },this);
46289         this.view.select(sels);
46290         return false;
46291     },
46292     
46293     
46294     
46295     onSelect : function(record, index){
46296        // Roo.log("onselect Called");
46297        // this is only called by the clear button now..
46298         this.view.clearSelections();
46299         this.setValue('[]');
46300         if (this.value != this.valueBefore) {
46301             this.fireEvent('change', this, this.value, this.valueBefore);
46302             this.valueBefore = this.value;
46303         }
46304     },
46305     getValueArray : function()
46306     {
46307         var ar = [] ;
46308         
46309         try {
46310             //Roo.log(this.value);
46311             if (typeof(this.value) == 'undefined') {
46312                 return [];
46313             }
46314             var ar = Roo.decode(this.value);
46315             return  ar instanceof Array ? ar : []; //?? valid?
46316             
46317         } catch(e) {
46318             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46319             return [];
46320         }
46321          
46322     },
46323     expand : function ()
46324     {
46325         
46326         Roo.form.ComboCheck.superclass.expand.call(this);
46327         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46328         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46329         
46330
46331     },
46332     
46333     collapse : function(){
46334         Roo.form.ComboCheck.superclass.collapse.call(this);
46335         var sl = this.view.getSelectedIndexes();
46336         var st = this.store;
46337         var nv = [];
46338         var tv = [];
46339         var r;
46340         Roo.each(sl, function(i) {
46341             r = st.getAt(i);
46342             nv.push(r.get(this.valueField));
46343         },this);
46344         this.setValue(Roo.encode(nv));
46345         if (this.value != this.valueBefore) {
46346
46347             this.fireEvent('change', this, this.value, this.valueBefore);
46348             this.valueBefore = this.value;
46349         }
46350         
46351     },
46352     
46353     setValue : function(v){
46354         // Roo.log(v);
46355         this.value = v;
46356         
46357         var vals = this.getValueArray();
46358         var tv = [];
46359         Roo.each(vals, function(k) {
46360             var r = this.findRecord(this.valueField, k);
46361             if(r){
46362                 tv.push(r.data[this.displayField]);
46363             }else if(this.valueNotFoundText !== undefined){
46364                 tv.push( this.valueNotFoundText );
46365             }
46366         },this);
46367        // Roo.log(tv);
46368         
46369         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46370         this.hiddenField.value = v;
46371         this.value = v;
46372     }
46373     
46374 });/*
46375  * Based on:
46376  * Ext JS Library 1.1.1
46377  * Copyright(c) 2006-2007, Ext JS, LLC.
46378  *
46379  * Originally Released Under LGPL - original licence link has changed is not relivant.
46380  *
46381  * Fork - LGPL
46382  * <script type="text/javascript">
46383  */
46384  
46385 /**
46386  * @class Roo.form.Signature
46387  * @extends Roo.form.Field
46388  * Signature field.  
46389  * @constructor
46390  * 
46391  * @param {Object} config Configuration options
46392  */
46393
46394 Roo.form.Signature = function(config){
46395     Roo.form.Signature.superclass.constructor.call(this, config);
46396     
46397     this.addEvents({// not in used??
46398          /**
46399          * @event confirm
46400          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46401              * @param {Roo.form.Signature} combo This combo box
46402              */
46403         'confirm' : true,
46404         /**
46405          * @event reset
46406          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46407              * @param {Roo.form.ComboBox} combo This combo box
46408              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46409              */
46410         'reset' : true
46411     });
46412 };
46413
46414 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46415     /**
46416      * @cfg {Object} labels Label to use when rendering a form.
46417      * defaults to 
46418      * labels : { 
46419      *      clear : "Clear",
46420      *      confirm : "Confirm"
46421      *  }
46422      */
46423     labels : { 
46424         clear : "Clear",
46425         confirm : "Confirm"
46426     },
46427     /**
46428      * @cfg {Number} width The signature panel width (defaults to 300)
46429      */
46430     width: 300,
46431     /**
46432      * @cfg {Number} height The signature panel height (defaults to 100)
46433      */
46434     height : 100,
46435     /**
46436      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46437      */
46438     allowBlank : false,
46439     
46440     //private
46441     // {Object} signPanel The signature SVG panel element (defaults to {})
46442     signPanel : {},
46443     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46444     isMouseDown : false,
46445     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46446     isConfirmed : false,
46447     // {String} signatureTmp SVG mapping string (defaults to empty string)
46448     signatureTmp : '',
46449     
46450     
46451     defaultAutoCreate : { // modified by initCompnoent..
46452         tag: "input",
46453         type:"hidden"
46454     },
46455
46456     // private
46457     onRender : function(ct, position){
46458         
46459         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46460         
46461         this.wrap = this.el.wrap({
46462             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46463         });
46464         
46465         this.createToolbar(this);
46466         this.signPanel = this.wrap.createChild({
46467                 tag: 'div',
46468                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46469             }, this.el
46470         );
46471             
46472         this.svgID = Roo.id();
46473         this.svgEl = this.signPanel.createChild({
46474               xmlns : 'http://www.w3.org/2000/svg',
46475               tag : 'svg',
46476               id : this.svgID + "-svg",
46477               width: this.width,
46478               height: this.height,
46479               viewBox: '0 0 '+this.width+' '+this.height,
46480               cn : [
46481                 {
46482                     tag: "rect",
46483                     id: this.svgID + "-svg-r",
46484                     width: this.width,
46485                     height: this.height,
46486                     fill: "#ffa"
46487                 },
46488                 {
46489                     tag: "line",
46490                     id: this.svgID + "-svg-l",
46491                     x1: "0", // start
46492                     y1: (this.height*0.8), // start set the line in 80% of height
46493                     x2: this.width, // end
46494                     y2: (this.height*0.8), // end set the line in 80% of height
46495                     'stroke': "#666",
46496                     'stroke-width': "1",
46497                     'stroke-dasharray': "3",
46498                     'shape-rendering': "crispEdges",
46499                     'pointer-events': "none"
46500                 },
46501                 {
46502                     tag: "path",
46503                     id: this.svgID + "-svg-p",
46504                     'stroke': "navy",
46505                     'stroke-width': "3",
46506                     'fill': "none",
46507                     'pointer-events': 'none'
46508                 }
46509               ]
46510         });
46511         this.createSVG();
46512         this.svgBox = this.svgEl.dom.getScreenCTM();
46513     },
46514     createSVG : function(){ 
46515         var svg = this.signPanel;
46516         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46517         var t = this;
46518
46519         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46520         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46521         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46522         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46523         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46524         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46525         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46526         
46527     },
46528     isTouchEvent : function(e){
46529         return e.type.match(/^touch/);
46530     },
46531     getCoords : function (e) {
46532         var pt    = this.svgEl.dom.createSVGPoint();
46533         pt.x = e.clientX; 
46534         pt.y = e.clientY;
46535         if (this.isTouchEvent(e)) {
46536             pt.x =  e.targetTouches[0].clientX 
46537             pt.y = e.targetTouches[0].clientY;
46538         }
46539         var a = this.svgEl.dom.getScreenCTM();
46540         var b = a.inverse();
46541         var mx = pt.matrixTransform(b);
46542         return mx.x + ',' + mx.y;
46543     },
46544     //mouse event headler 
46545     down : function (e) {
46546         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46547         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46548         
46549         this.isMouseDown = true;
46550         
46551         e.preventDefault();
46552     },
46553     move : function (e) {
46554         if (this.isMouseDown) {
46555             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46556             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46557         }
46558         
46559         e.preventDefault();
46560     },
46561     up : function (e) {
46562         this.isMouseDown = false;
46563         var sp = this.signatureTmp.split(' ');
46564         
46565         if(sp.length > 1){
46566             if(!sp[sp.length-2].match(/^L/)){
46567                 sp.pop();
46568                 sp.pop();
46569                 sp.push("");
46570                 this.signatureTmp = sp.join(" ");
46571             }
46572         }
46573         if(this.getValue() != this.signatureTmp){
46574             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46575             this.isConfirmed = false;
46576         }
46577         e.preventDefault();
46578     },
46579     
46580     /**
46581      * Protected method that will not generally be called directly. It
46582      * is called when the editor creates its toolbar. Override this method if you need to
46583      * add custom toolbar buttons.
46584      * @param {HtmlEditor} editor
46585      */
46586     createToolbar : function(editor){
46587          function btn(id, toggle, handler){
46588             var xid = fid + '-'+ id ;
46589             return {
46590                 id : xid,
46591                 cmd : id,
46592                 cls : 'x-btn-icon x-edit-'+id,
46593                 enableToggle:toggle !== false,
46594                 scope: editor, // was editor...
46595                 handler:handler||editor.relayBtnCmd,
46596                 clickEvent:'mousedown',
46597                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46598                 tabIndex:-1
46599             };
46600         }
46601         
46602         
46603         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46604         this.tb = tb;
46605         this.tb.add(
46606            {
46607                 cls : ' x-signature-btn x-signature-'+id,
46608                 scope: editor, // was editor...
46609                 handler: this.reset,
46610                 clickEvent:'mousedown',
46611                 text: this.labels.clear
46612             },
46613             {
46614                  xtype : 'Fill',
46615                  xns: Roo.Toolbar
46616             }, 
46617             {
46618                 cls : '  x-signature-btn x-signature-'+id,
46619                 scope: editor, // was editor...
46620                 handler: this.confirmHandler,
46621                 clickEvent:'mousedown',
46622                 text: this.labels.confirm
46623             }
46624         );
46625     
46626     },
46627     //public
46628     /**
46629      * when user is clicked confirm then show this image.....
46630      * 
46631      * @return {String} Image Data URI
46632      */
46633     getImageDataURI : function(){
46634         var svg = this.svgEl.dom.parentNode.innerHTML;
46635         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46636         return src; 
46637     },
46638     /**
46639      * 
46640      * @return {Boolean} this.isConfirmed
46641      */
46642     getConfirmed : function(){
46643         return this.isConfirmed;
46644     },
46645     /**
46646      * 
46647      * @return {Number} this.width
46648      */
46649     getWidth : function(){
46650         return this.width;
46651     },
46652     /**
46653      * 
46654      * @return {Number} this.height
46655      */
46656     getHeight : function(){
46657         return this.height;
46658     },
46659     // private
46660     getSignature : function(){
46661         return this.signatureTmp;
46662     },
46663     // private
46664     reset : function(){
46665         this.signatureTmp = '';
46666         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46667         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46668         this.isConfirmed = false;
46669         Roo.form.Signature.superclass.reset.call(this);
46670     },
46671     setSignature : function(s){
46672         this.signatureTmp = s;
46673         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46674         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46675         this.setValue(s);
46676         this.isConfirmed = false;
46677         Roo.form.Signature.superclass.reset.call(this);
46678     }, 
46679     test : function(){
46680 //        Roo.log(this.signPanel.dom.contentWindow.up())
46681     },
46682     //private
46683     setConfirmed : function(){
46684         
46685         
46686         
46687 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46688     },
46689     // private
46690     confirmHandler : function(){
46691         if(!this.getSignature()){
46692             return;
46693         }
46694         
46695         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46696         this.setValue(this.getSignature());
46697         this.isConfirmed = true;
46698         
46699         this.fireEvent('confirm', this);
46700     },
46701     // private
46702     // Subclasses should provide the validation implementation by overriding this
46703     validateValue : function(value){
46704         if(this.allowBlank){
46705             return true;
46706         }
46707         
46708         if(this.isConfirmed){
46709             return true;
46710         }
46711         return false;
46712     }
46713 });/*
46714  * Based on:
46715  * Ext JS Library 1.1.1
46716  * Copyright(c) 2006-2007, Ext JS, LLC.
46717  *
46718  * Originally Released Under LGPL - original licence link has changed is not relivant.
46719  *
46720  * Fork - LGPL
46721  * <script type="text/javascript">
46722  */
46723  
46724
46725 /**
46726  * @class Roo.form.ComboBox
46727  * @extends Roo.form.TriggerField
46728  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46729  * @constructor
46730  * Create a new ComboBox.
46731  * @param {Object} config Configuration options
46732  */
46733 Roo.form.Select = function(config){
46734     Roo.form.Select.superclass.constructor.call(this, config);
46735      
46736 };
46737
46738 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46739     /**
46740      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46741      */
46742     /**
46743      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46744      * rendering into an Roo.Editor, defaults to false)
46745      */
46746     /**
46747      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46748      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46749      */
46750     /**
46751      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46752      */
46753     /**
46754      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46755      * the dropdown list (defaults to undefined, with no header element)
46756      */
46757
46758      /**
46759      * @cfg {String/Roo.Template} tpl The template to use to render the output
46760      */
46761      
46762     // private
46763     defaultAutoCreate : {tag: "select"  },
46764     /**
46765      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46766      */
46767     listWidth: undefined,
46768     /**
46769      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46770      * mode = 'remote' or 'text' if mode = 'local')
46771      */
46772     displayField: undefined,
46773     /**
46774      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46775      * mode = 'remote' or 'value' if mode = 'local'). 
46776      * Note: use of a valueField requires the user make a selection
46777      * in order for a value to be mapped.
46778      */
46779     valueField: undefined,
46780     
46781     
46782     /**
46783      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46784      * field's data value (defaults to the underlying DOM element's name)
46785      */
46786     hiddenName: undefined,
46787     /**
46788      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46789      */
46790     listClass: '',
46791     /**
46792      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46793      */
46794     selectedClass: 'x-combo-selected',
46795     /**
46796      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46797      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46798      * which displays a downward arrow icon).
46799      */
46800     triggerClass : 'x-form-arrow-trigger',
46801     /**
46802      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46803      */
46804     shadow:'sides',
46805     /**
46806      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46807      * anchor positions (defaults to 'tl-bl')
46808      */
46809     listAlign: 'tl-bl?',
46810     /**
46811      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46812      */
46813     maxHeight: 300,
46814     /**
46815      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46816      * query specified by the allQuery config option (defaults to 'query')
46817      */
46818     triggerAction: 'query',
46819     /**
46820      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46821      * (defaults to 4, does not apply if editable = false)
46822      */
46823     minChars : 4,
46824     /**
46825      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46826      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46827      */
46828     typeAhead: false,
46829     /**
46830      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46831      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46832      */
46833     queryDelay: 500,
46834     /**
46835      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46836      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46837      */
46838     pageSize: 0,
46839     /**
46840      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46841      * when editable = true (defaults to false)
46842      */
46843     selectOnFocus:false,
46844     /**
46845      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46846      */
46847     queryParam: 'query',
46848     /**
46849      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46850      * when mode = 'remote' (defaults to 'Loading...')
46851      */
46852     loadingText: 'Loading...',
46853     /**
46854      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46855      */
46856     resizable: false,
46857     /**
46858      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46859      */
46860     handleHeight : 8,
46861     /**
46862      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46863      * traditional select (defaults to true)
46864      */
46865     editable: true,
46866     /**
46867      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46868      */
46869     allQuery: '',
46870     /**
46871      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46872      */
46873     mode: 'remote',
46874     /**
46875      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46876      * listWidth has a higher value)
46877      */
46878     minListWidth : 70,
46879     /**
46880      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46881      * allow the user to set arbitrary text into the field (defaults to false)
46882      */
46883     forceSelection:false,
46884     /**
46885      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46886      * if typeAhead = true (defaults to 250)
46887      */
46888     typeAheadDelay : 250,
46889     /**
46890      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46891      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46892      */
46893     valueNotFoundText : undefined,
46894     
46895     /**
46896      * @cfg {String} defaultValue The value displayed after loading the store.
46897      */
46898     defaultValue: '',
46899     
46900     /**
46901      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46902      */
46903     blockFocus : false,
46904     
46905     /**
46906      * @cfg {Boolean} disableClear Disable showing of clear button.
46907      */
46908     disableClear : false,
46909     /**
46910      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46911      */
46912     alwaysQuery : false,
46913     
46914     //private
46915     addicon : false,
46916     editicon: false,
46917     
46918     // element that contains real text value.. (when hidden is used..)
46919      
46920     // private
46921     onRender : function(ct, position){
46922         Roo.form.Field.prototype.onRender.call(this, ct, position);
46923         
46924         if(this.store){
46925             this.store.on('beforeload', this.onBeforeLoad, this);
46926             this.store.on('load', this.onLoad, this);
46927             this.store.on('loadexception', this.onLoadException, this);
46928             this.store.load({});
46929         }
46930         
46931         
46932         
46933     },
46934
46935     // private
46936     initEvents : function(){
46937         //Roo.form.ComboBox.superclass.initEvents.call(this);
46938  
46939     },
46940
46941     onDestroy : function(){
46942        
46943         if(this.store){
46944             this.store.un('beforeload', this.onBeforeLoad, this);
46945             this.store.un('load', this.onLoad, this);
46946             this.store.un('loadexception', this.onLoadException, this);
46947         }
46948         //Roo.form.ComboBox.superclass.onDestroy.call(this);
46949     },
46950
46951     // private
46952     fireKey : function(e){
46953         if(e.isNavKeyPress() && !this.list.isVisible()){
46954             this.fireEvent("specialkey", this, e);
46955         }
46956     },
46957
46958     // private
46959     onResize: function(w, h){
46960         
46961         return; 
46962     
46963         
46964     },
46965
46966     /**
46967      * Allow or prevent the user from directly editing the field text.  If false is passed,
46968      * the user will only be able to select from the items defined in the dropdown list.  This method
46969      * is the runtime equivalent of setting the 'editable' config option at config time.
46970      * @param {Boolean} value True to allow the user to directly edit the field text
46971      */
46972     setEditable : function(value){
46973          
46974     },
46975
46976     // private
46977     onBeforeLoad : function(){
46978         
46979         Roo.log("Select before load");
46980         return;
46981     
46982         this.innerList.update(this.loadingText ?
46983                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
46984         //this.restrictHeight();
46985         this.selectedIndex = -1;
46986     },
46987
46988     // private
46989     onLoad : function(){
46990
46991     
46992         var dom = this.el.dom;
46993         dom.innerHTML = '';
46994          var od = dom.ownerDocument;
46995          
46996         if (this.emptyText) {
46997             var op = od.createElement('option');
46998             op.setAttribute('value', '');
46999             op.innerHTML = String.format('{0}', this.emptyText);
47000             dom.appendChild(op);
47001         }
47002         if(this.store.getCount() > 0){
47003            
47004             var vf = this.valueField;
47005             var df = this.displayField;
47006             this.store.data.each(function(r) {
47007                 // which colmsn to use... testing - cdoe / title..
47008                 var op = od.createElement('option');
47009                 op.setAttribute('value', r.data[vf]);
47010                 op.innerHTML = String.format('{0}', r.data[df]);
47011                 dom.appendChild(op);
47012             });
47013             if (typeof(this.defaultValue != 'undefined')) {
47014                 this.setValue(this.defaultValue);
47015             }
47016             
47017              
47018         }else{
47019             //this.onEmptyResults();
47020         }
47021         //this.el.focus();
47022     },
47023     // private
47024     onLoadException : function()
47025     {
47026         dom.innerHTML = '';
47027             
47028         Roo.log("Select on load exception");
47029         return;
47030     
47031         this.collapse();
47032         Roo.log(this.store.reader.jsonData);
47033         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47034             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47035         }
47036         
47037         
47038     },
47039     // private
47040     onTypeAhead : function(){
47041          
47042     },
47043
47044     // private
47045     onSelect : function(record, index){
47046         Roo.log('on select?');
47047         return;
47048         if(this.fireEvent('beforeselect', this, record, index) !== false){
47049             this.setFromData(index > -1 ? record.data : false);
47050             this.collapse();
47051             this.fireEvent('select', this, record, index);
47052         }
47053     },
47054
47055     /**
47056      * Returns the currently selected field value or empty string if no value is set.
47057      * @return {String} value The selected value
47058      */
47059     getValue : function(){
47060         var dom = this.el.dom;
47061         this.value = dom.options[dom.selectedIndex].value;
47062         return this.value;
47063         
47064     },
47065
47066     /**
47067      * Clears any text/value currently set in the field
47068      */
47069     clearValue : function(){
47070         this.value = '';
47071         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47072         
47073     },
47074
47075     /**
47076      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47077      * will be displayed in the field.  If the value does not match the data value of an existing item,
47078      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47079      * Otherwise the field will be blank (although the value will still be set).
47080      * @param {String} value The value to match
47081      */
47082     setValue : function(v){
47083         var d = this.el.dom;
47084         for (var i =0; i < d.options.length;i++) {
47085             if (v == d.options[i].value) {
47086                 d.selectedIndex = i;
47087                 this.value = v;
47088                 return;
47089             }
47090         }
47091         this.clearValue();
47092     },
47093     /**
47094      * @property {Object} the last set data for the element
47095      */
47096     
47097     lastData : false,
47098     /**
47099      * Sets the value of the field based on a object which is related to the record format for the store.
47100      * @param {Object} value the value to set as. or false on reset?
47101      */
47102     setFromData : function(o){
47103         Roo.log('setfrom data?');
47104          
47105         
47106         
47107     },
47108     // private
47109     reset : function(){
47110         this.clearValue();
47111     },
47112     // private
47113     findRecord : function(prop, value){
47114         
47115         return false;
47116     
47117         var record;
47118         if(this.store.getCount() > 0){
47119             this.store.each(function(r){
47120                 if(r.data[prop] == value){
47121                     record = r;
47122                     return false;
47123                 }
47124                 return true;
47125             });
47126         }
47127         return record;
47128     },
47129     
47130     getName: function()
47131     {
47132         // returns hidden if it's set..
47133         if (!this.rendered) {return ''};
47134         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47135         
47136     },
47137      
47138
47139     
47140
47141     // private
47142     onEmptyResults : function(){
47143         Roo.log('empty results');
47144         //this.collapse();
47145     },
47146
47147     /**
47148      * Returns true if the dropdown list is expanded, else false.
47149      */
47150     isExpanded : function(){
47151         return false;
47152     },
47153
47154     /**
47155      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47156      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47157      * @param {String} value The data value of the item to select
47158      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47159      * selected item if it is not currently in view (defaults to true)
47160      * @return {Boolean} True if the value matched an item in the list, else false
47161      */
47162     selectByValue : function(v, scrollIntoView){
47163         Roo.log('select By Value');
47164         return false;
47165     
47166         if(v !== undefined && v !== null){
47167             var r = this.findRecord(this.valueField || this.displayField, v);
47168             if(r){
47169                 this.select(this.store.indexOf(r), scrollIntoView);
47170                 return true;
47171             }
47172         }
47173         return false;
47174     },
47175
47176     /**
47177      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47178      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47179      * @param {Number} index The zero-based index of the list item to select
47180      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47181      * selected item if it is not currently in view (defaults to true)
47182      */
47183     select : function(index, scrollIntoView){
47184         Roo.log('select ');
47185         return  ;
47186         
47187         this.selectedIndex = index;
47188         this.view.select(index);
47189         if(scrollIntoView !== false){
47190             var el = this.view.getNode(index);
47191             if(el){
47192                 this.innerList.scrollChildIntoView(el, false);
47193             }
47194         }
47195     },
47196
47197       
47198
47199     // private
47200     validateBlur : function(){
47201         
47202         return;
47203         
47204     },
47205
47206     // private
47207     initQuery : function(){
47208         this.doQuery(this.getRawValue());
47209     },
47210
47211     // private
47212     doForce : function(){
47213         if(this.el.dom.value.length > 0){
47214             this.el.dom.value =
47215                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47216              
47217         }
47218     },
47219
47220     /**
47221      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47222      * query allowing the query action to be canceled if needed.
47223      * @param {String} query The SQL query to execute
47224      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47225      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47226      * saved in the current store (defaults to false)
47227      */
47228     doQuery : function(q, forceAll){
47229         
47230         Roo.log('doQuery?');
47231         if(q === undefined || q === null){
47232             q = '';
47233         }
47234         var qe = {
47235             query: q,
47236             forceAll: forceAll,
47237             combo: this,
47238             cancel:false
47239         };
47240         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47241             return false;
47242         }
47243         q = qe.query;
47244         forceAll = qe.forceAll;
47245         if(forceAll === true || (q.length >= this.minChars)){
47246             if(this.lastQuery != q || this.alwaysQuery){
47247                 this.lastQuery = q;
47248                 if(this.mode == 'local'){
47249                     this.selectedIndex = -1;
47250                     if(forceAll){
47251                         this.store.clearFilter();
47252                     }else{
47253                         this.store.filter(this.displayField, q);
47254                     }
47255                     this.onLoad();
47256                 }else{
47257                     this.store.baseParams[this.queryParam] = q;
47258                     this.store.load({
47259                         params: this.getParams(q)
47260                     });
47261                     this.expand();
47262                 }
47263             }else{
47264                 this.selectedIndex = -1;
47265                 this.onLoad();   
47266             }
47267         }
47268     },
47269
47270     // private
47271     getParams : function(q){
47272         var p = {};
47273         //p[this.queryParam] = q;
47274         if(this.pageSize){
47275             p.start = 0;
47276             p.limit = this.pageSize;
47277         }
47278         return p;
47279     },
47280
47281     /**
47282      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47283      */
47284     collapse : function(){
47285         
47286     },
47287
47288     // private
47289     collapseIf : function(e){
47290         
47291     },
47292
47293     /**
47294      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47295      */
47296     expand : function(){
47297         
47298     } ,
47299
47300     // private
47301      
47302
47303     /** 
47304     * @cfg {Boolean} grow 
47305     * @hide 
47306     */
47307     /** 
47308     * @cfg {Number} growMin 
47309     * @hide 
47310     */
47311     /** 
47312     * @cfg {Number} growMax 
47313     * @hide 
47314     */
47315     /**
47316      * @hide
47317      * @method autoSize
47318      */
47319     
47320     setWidth : function()
47321     {
47322         
47323     },
47324     getResizeEl : function(){
47325         return this.el;
47326     }
47327 });//<script type="text/javasscript">
47328  
47329
47330 /**
47331  * @class Roo.DDView
47332  * A DnD enabled version of Roo.View.
47333  * @param {Element/String} container The Element in which to create the View.
47334  * @param {String} tpl The template string used to create the markup for each element of the View
47335  * @param {Object} config The configuration properties. These include all the config options of
47336  * {@link Roo.View} plus some specific to this class.<br>
47337  * <p>
47338  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47339  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47340  * <p>
47341  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47342 .x-view-drag-insert-above {
47343         border-top:1px dotted #3366cc;
47344 }
47345 .x-view-drag-insert-below {
47346         border-bottom:1px dotted #3366cc;
47347 }
47348 </code></pre>
47349  * 
47350  */
47351  
47352 Roo.DDView = function(container, tpl, config) {
47353     Roo.DDView.superclass.constructor.apply(this, arguments);
47354     this.getEl().setStyle("outline", "0px none");
47355     this.getEl().unselectable();
47356     if (this.dragGroup) {
47357                 this.setDraggable(this.dragGroup.split(","));
47358     }
47359     if (this.dropGroup) {
47360                 this.setDroppable(this.dropGroup.split(","));
47361     }
47362     if (this.deletable) {
47363         this.setDeletable();
47364     }
47365     this.isDirtyFlag = false;
47366         this.addEvents({
47367                 "drop" : true
47368         });
47369 };
47370
47371 Roo.extend(Roo.DDView, Roo.View, {
47372 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47373 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47374 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47375 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47376
47377         isFormField: true,
47378
47379         reset: Roo.emptyFn,
47380         
47381         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47382
47383         validate: function() {
47384                 return true;
47385         },
47386         
47387         destroy: function() {
47388                 this.purgeListeners();
47389                 this.getEl.removeAllListeners();
47390                 this.getEl().remove();
47391                 if (this.dragZone) {
47392                         if (this.dragZone.destroy) {
47393                                 this.dragZone.destroy();
47394                         }
47395                 }
47396                 if (this.dropZone) {
47397                         if (this.dropZone.destroy) {
47398                                 this.dropZone.destroy();
47399                         }
47400                 }
47401         },
47402
47403 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47404         getName: function() {
47405                 return this.name;
47406         },
47407
47408 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47409         setValue: function(v) {
47410                 if (!this.store) {
47411                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47412                 }
47413                 var data = {};
47414                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47415                 this.store.proxy = new Roo.data.MemoryProxy(data);
47416                 this.store.load();
47417         },
47418
47419 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47420         getValue: function() {
47421                 var result = '(';
47422                 this.store.each(function(rec) {
47423                         result += rec.id + ',';
47424                 });
47425                 return result.substr(0, result.length - 1) + ')';
47426         },
47427         
47428         getIds: function() {
47429                 var i = 0, result = new Array(this.store.getCount());
47430                 this.store.each(function(rec) {
47431                         result[i++] = rec.id;
47432                 });
47433                 return result;
47434         },
47435         
47436         isDirty: function() {
47437                 return this.isDirtyFlag;
47438         },
47439
47440 /**
47441  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47442  *      whole Element becomes the target, and this causes the drop gesture to append.
47443  */
47444     getTargetFromEvent : function(e) {
47445                 var target = e.getTarget();
47446                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47447                 target = target.parentNode;
47448                 }
47449                 if (!target) {
47450                         target = this.el.dom.lastChild || this.el.dom;
47451                 }
47452                 return target;
47453     },
47454
47455 /**
47456  *      Create the drag data which consists of an object which has the property "ddel" as
47457  *      the drag proxy element. 
47458  */
47459     getDragData : function(e) {
47460         var target = this.findItemFromChild(e.getTarget());
47461                 if(target) {
47462                         this.handleSelection(e);
47463                         var selNodes = this.getSelectedNodes();
47464             var dragData = {
47465                 source: this,
47466                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47467                 nodes: selNodes,
47468                 records: []
47469                         };
47470                         var selectedIndices = this.getSelectedIndexes();
47471                         for (var i = 0; i < selectedIndices.length; i++) {
47472                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47473                         }
47474                         if (selNodes.length == 1) {
47475                                 dragData.ddel = target.cloneNode(true); // the div element
47476                         } else {
47477                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47478                                 div.className = 'multi-proxy';
47479                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47480                                         div.appendChild(selNodes[i].cloneNode(true));
47481                                 }
47482                                 dragData.ddel = div;
47483                         }
47484             //console.log(dragData)
47485             //console.log(dragData.ddel.innerHTML)
47486                         return dragData;
47487                 }
47488         //console.log('nodragData')
47489                 return false;
47490     },
47491     
47492 /**     Specify to which ddGroup items in this DDView may be dragged. */
47493     setDraggable: function(ddGroup) {
47494         if (ddGroup instanceof Array) {
47495                 Roo.each(ddGroup, this.setDraggable, this);
47496                 return;
47497         }
47498         if (this.dragZone) {
47499                 this.dragZone.addToGroup(ddGroup);
47500         } else {
47501                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47502                                 containerScroll: true,
47503                                 ddGroup: ddGroup 
47504
47505                         });
47506 //                      Draggability implies selection. DragZone's mousedown selects the element.
47507                         if (!this.multiSelect) { this.singleSelect = true; }
47508
47509 //                      Wire the DragZone's handlers up to methods in *this*
47510                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47511                 }
47512     },
47513
47514 /**     Specify from which ddGroup this DDView accepts drops. */
47515     setDroppable: function(ddGroup) {
47516         if (ddGroup instanceof Array) {
47517                 Roo.each(ddGroup, this.setDroppable, this);
47518                 return;
47519         }
47520         if (this.dropZone) {
47521                 this.dropZone.addToGroup(ddGroup);
47522         } else {
47523                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47524                                 containerScroll: true,
47525                                 ddGroup: ddGroup
47526                         });
47527
47528 //                      Wire the DropZone's handlers up to methods in *this*
47529                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47530                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47531                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47532                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47533                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47534                 }
47535     },
47536
47537 /**     Decide whether to drop above or below a View node. */
47538     getDropPoint : function(e, n, dd){
47539         if (n == this.el.dom) { return "above"; }
47540                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47541                 var c = t + (b - t) / 2;
47542                 var y = Roo.lib.Event.getPageY(e);
47543                 if(y <= c) {
47544                         return "above";
47545                 }else{
47546                         return "below";
47547                 }
47548     },
47549
47550     onNodeEnter : function(n, dd, e, data){
47551                 return false;
47552     },
47553     
47554     onNodeOver : function(n, dd, e, data){
47555                 var pt = this.getDropPoint(e, n, dd);
47556                 // set the insert point style on the target node
47557                 var dragElClass = this.dropNotAllowed;
47558                 if (pt) {
47559                         var targetElClass;
47560                         if (pt == "above"){
47561                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47562                                 targetElClass = "x-view-drag-insert-above";
47563                         } else {
47564                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47565                                 targetElClass = "x-view-drag-insert-below";
47566                         }
47567                         if (this.lastInsertClass != targetElClass){
47568                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47569                                 this.lastInsertClass = targetElClass;
47570                         }
47571                 }
47572                 return dragElClass;
47573         },
47574
47575     onNodeOut : function(n, dd, e, data){
47576                 this.removeDropIndicators(n);
47577     },
47578
47579     onNodeDrop : function(n, dd, e, data){
47580         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47581                 return false;
47582         }
47583         var pt = this.getDropPoint(e, n, dd);
47584                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47585                 if (pt == "below") { insertAt++; }
47586                 for (var i = 0; i < data.records.length; i++) {
47587                         var r = data.records[i];
47588                         var dup = this.store.getById(r.id);
47589                         if (dup && (dd != this.dragZone)) {
47590                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47591                         } else {
47592                                 if (data.copy) {
47593                                         this.store.insert(insertAt++, r.copy());
47594                                 } else {
47595                                         data.source.isDirtyFlag = true;
47596                                         r.store.remove(r);
47597                                         this.store.insert(insertAt++, r);
47598                                 }
47599                                 this.isDirtyFlag = true;
47600                         }
47601                 }
47602                 this.dragZone.cachedTarget = null;
47603                 return true;
47604     },
47605
47606     removeDropIndicators : function(n){
47607                 if(n){
47608                         Roo.fly(n).removeClass([
47609                                 "x-view-drag-insert-above",
47610                                 "x-view-drag-insert-below"]);
47611                         this.lastInsertClass = "_noclass";
47612                 }
47613     },
47614
47615 /**
47616  *      Utility method. Add a delete option to the DDView's context menu.
47617  *      @param {String} imageUrl The URL of the "delete" icon image.
47618  */
47619         setDeletable: function(imageUrl) {
47620                 if (!this.singleSelect && !this.multiSelect) {
47621                         this.singleSelect = true;
47622                 }
47623                 var c = this.getContextMenu();
47624                 this.contextMenu.on("itemclick", function(item) {
47625                         switch (item.id) {
47626                                 case "delete":
47627                                         this.remove(this.getSelectedIndexes());
47628                                         break;
47629                         }
47630                 }, this);
47631                 this.contextMenu.add({
47632                         icon: imageUrl,
47633                         id: "delete",
47634                         text: 'Delete'
47635                 });
47636         },
47637         
47638 /**     Return the context menu for this DDView. */
47639         getContextMenu: function() {
47640                 if (!this.contextMenu) {
47641 //                      Create the View's context menu
47642                         this.contextMenu = new Roo.menu.Menu({
47643                                 id: this.id + "-contextmenu"
47644                         });
47645                         this.el.on("contextmenu", this.showContextMenu, this);
47646                 }
47647                 return this.contextMenu;
47648         },
47649         
47650         disableContextMenu: function() {
47651                 if (this.contextMenu) {
47652                         this.el.un("contextmenu", this.showContextMenu, this);
47653                 }
47654         },
47655
47656         showContextMenu: function(e, item) {
47657         item = this.findItemFromChild(e.getTarget());
47658                 if (item) {
47659                         e.stopEvent();
47660                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47661                         this.contextMenu.showAt(e.getXY());
47662             }
47663     },
47664
47665 /**
47666  *      Remove {@link Roo.data.Record}s at the specified indices.
47667  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47668  */
47669     remove: function(selectedIndices) {
47670                 selectedIndices = [].concat(selectedIndices);
47671                 for (var i = 0; i < selectedIndices.length; i++) {
47672                         var rec = this.store.getAt(selectedIndices[i]);
47673                         this.store.remove(rec);
47674                 }
47675     },
47676
47677 /**
47678  *      Double click fires the event, but also, if this is draggable, and there is only one other
47679  *      related DropZone, it transfers the selected node.
47680  */
47681     onDblClick : function(e){
47682         var item = this.findItemFromChild(e.getTarget());
47683         if(item){
47684             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47685                 return false;
47686             }
47687             if (this.dragGroup) {
47688                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47689                     while (targets.indexOf(this.dropZone) > -1) {
47690                             targets.remove(this.dropZone);
47691                                 }
47692                     if (targets.length == 1) {
47693                                         this.dragZone.cachedTarget = null;
47694                         var el = Roo.get(targets[0].getEl());
47695                         var box = el.getBox(true);
47696                         targets[0].onNodeDrop(el.dom, {
47697                                 target: el.dom,
47698                                 xy: [box.x, box.y + box.height - 1]
47699                         }, null, this.getDragData(e));
47700                     }
47701                 }
47702         }
47703     },
47704     
47705     handleSelection: function(e) {
47706                 this.dragZone.cachedTarget = null;
47707         var item = this.findItemFromChild(e.getTarget());
47708         if (!item) {
47709                 this.clearSelections(true);
47710                 return;
47711         }
47712                 if (item && (this.multiSelect || this.singleSelect)){
47713                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47714                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47715                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47716                                 this.unselect(item);
47717                         } else {
47718                                 this.select(item, this.multiSelect && e.ctrlKey);
47719                                 this.lastSelection = item;
47720                         }
47721                 }
47722     },
47723
47724     onItemClick : function(item, index, e){
47725                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47726                         return false;
47727                 }
47728                 return true;
47729     },
47730
47731     unselect : function(nodeInfo, suppressEvent){
47732                 var node = this.getNode(nodeInfo);
47733                 if(node && this.isSelected(node)){
47734                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47735                                 Roo.fly(node).removeClass(this.selectedClass);
47736                                 this.selections.remove(node);
47737                                 if(!suppressEvent){
47738                                         this.fireEvent("selectionchange", this, this.selections);
47739                                 }
47740                         }
47741                 }
47742     }
47743 });
47744 /*
47745  * Based on:
47746  * Ext JS Library 1.1.1
47747  * Copyright(c) 2006-2007, Ext JS, LLC.
47748  *
47749  * Originally Released Under LGPL - original licence link has changed is not relivant.
47750  *
47751  * Fork - LGPL
47752  * <script type="text/javascript">
47753  */
47754  
47755 /**
47756  * @class Roo.LayoutManager
47757  * @extends Roo.util.Observable
47758  * Base class for layout managers.
47759  */
47760 Roo.LayoutManager = function(container, config){
47761     Roo.LayoutManager.superclass.constructor.call(this);
47762     this.el = Roo.get(container);
47763     // ie scrollbar fix
47764     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47765         document.body.scroll = "no";
47766     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47767         this.el.position('relative');
47768     }
47769     this.id = this.el.id;
47770     this.el.addClass("x-layout-container");
47771     /** false to disable window resize monitoring @type Boolean */
47772     this.monitorWindowResize = true;
47773     this.regions = {};
47774     this.addEvents({
47775         /**
47776          * @event layout
47777          * Fires when a layout is performed. 
47778          * @param {Roo.LayoutManager} this
47779          */
47780         "layout" : true,
47781         /**
47782          * @event regionresized
47783          * Fires when the user resizes a region. 
47784          * @param {Roo.LayoutRegion} region The resized region
47785          * @param {Number} newSize The new size (width for east/west, height for north/south)
47786          */
47787         "regionresized" : true,
47788         /**
47789          * @event regioncollapsed
47790          * Fires when a region is collapsed. 
47791          * @param {Roo.LayoutRegion} region The collapsed region
47792          */
47793         "regioncollapsed" : true,
47794         /**
47795          * @event regionexpanded
47796          * Fires when a region is expanded.  
47797          * @param {Roo.LayoutRegion} region The expanded region
47798          */
47799         "regionexpanded" : true
47800     });
47801     this.updating = false;
47802     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47803 };
47804
47805 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47806     /**
47807      * Returns true if this layout is currently being updated
47808      * @return {Boolean}
47809      */
47810     isUpdating : function(){
47811         return this.updating; 
47812     },
47813     
47814     /**
47815      * Suspend the LayoutManager from doing auto-layouts while
47816      * making multiple add or remove calls
47817      */
47818     beginUpdate : function(){
47819         this.updating = true;    
47820     },
47821     
47822     /**
47823      * Restore auto-layouts and optionally disable the manager from performing a layout
47824      * @param {Boolean} noLayout true to disable a layout update 
47825      */
47826     endUpdate : function(noLayout){
47827         this.updating = false;
47828         if(!noLayout){
47829             this.layout();
47830         }    
47831     },
47832     
47833     layout: function(){
47834         
47835     },
47836     
47837     onRegionResized : function(region, newSize){
47838         this.fireEvent("regionresized", region, newSize);
47839         this.layout();
47840     },
47841     
47842     onRegionCollapsed : function(region){
47843         this.fireEvent("regioncollapsed", region);
47844     },
47845     
47846     onRegionExpanded : function(region){
47847         this.fireEvent("regionexpanded", region);
47848     },
47849         
47850     /**
47851      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47852      * performs box-model adjustments.
47853      * @return {Object} The size as an object {width: (the width), height: (the height)}
47854      */
47855     getViewSize : function(){
47856         var size;
47857         if(this.el.dom != document.body){
47858             size = this.el.getSize();
47859         }else{
47860             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47861         }
47862         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47863         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47864         return size;
47865     },
47866     
47867     /**
47868      * Returns the Element this layout is bound to.
47869      * @return {Roo.Element}
47870      */
47871     getEl : function(){
47872         return this.el;
47873     },
47874     
47875     /**
47876      * Returns the specified region.
47877      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47878      * @return {Roo.LayoutRegion}
47879      */
47880     getRegion : function(target){
47881         return this.regions[target.toLowerCase()];
47882     },
47883     
47884     onWindowResize : function(){
47885         if(this.monitorWindowResize){
47886             this.layout();
47887         }
47888     }
47889 });/*
47890  * Based on:
47891  * Ext JS Library 1.1.1
47892  * Copyright(c) 2006-2007, Ext JS, LLC.
47893  *
47894  * Originally Released Under LGPL - original licence link has changed is not relivant.
47895  *
47896  * Fork - LGPL
47897  * <script type="text/javascript">
47898  */
47899 /**
47900  * @class Roo.BorderLayout
47901  * @extends Roo.LayoutManager
47902  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47903  * please see: <br><br>
47904  * <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>
47905  * <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>
47906  * Example:
47907  <pre><code>
47908  var layout = new Roo.BorderLayout(document.body, {
47909     north: {
47910         initialSize: 25,
47911         titlebar: false
47912     },
47913     west: {
47914         split:true,
47915         initialSize: 200,
47916         minSize: 175,
47917         maxSize: 400,
47918         titlebar: true,
47919         collapsible: true
47920     },
47921     east: {
47922         split:true,
47923         initialSize: 202,
47924         minSize: 175,
47925         maxSize: 400,
47926         titlebar: true,
47927         collapsible: true
47928     },
47929     south: {
47930         split:true,
47931         initialSize: 100,
47932         minSize: 100,
47933         maxSize: 200,
47934         titlebar: true,
47935         collapsible: true
47936     },
47937     center: {
47938         titlebar: true,
47939         autoScroll:true,
47940         resizeTabs: true,
47941         minTabWidth: 50,
47942         preferredTabWidth: 150
47943     }
47944 });
47945
47946 // shorthand
47947 var CP = Roo.ContentPanel;
47948
47949 layout.beginUpdate();
47950 layout.add("north", new CP("north", "North"));
47951 layout.add("south", new CP("south", {title: "South", closable: true}));
47952 layout.add("west", new CP("west", {title: "West"}));
47953 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
47954 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
47955 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
47956 layout.getRegion("center").showPanel("center1");
47957 layout.endUpdate();
47958 </code></pre>
47959
47960 <b>The container the layout is rendered into can be either the body element or any other element.
47961 If it is not the body element, the container needs to either be an absolute positioned element,
47962 or you will need to add "position:relative" to the css of the container.  You will also need to specify
47963 the container size if it is not the body element.</b>
47964
47965 * @constructor
47966 * Create a new BorderLayout
47967 * @param {String/HTMLElement/Element} container The container this layout is bound to
47968 * @param {Object} config Configuration options
47969  */
47970 Roo.BorderLayout = function(container, config){
47971     config = config || {};
47972     Roo.BorderLayout.superclass.constructor.call(this, container, config);
47973     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
47974     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
47975         var target = this.factory.validRegions[i];
47976         if(config[target]){
47977             this.addRegion(target, config[target]);
47978         }
47979     }
47980 };
47981
47982 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
47983     /**
47984      * Creates and adds a new region if it doesn't already exist.
47985      * @param {String} target The target region key (north, south, east, west or center).
47986      * @param {Object} config The regions config object
47987      * @return {BorderLayoutRegion} The new region
47988      */
47989     addRegion : function(target, config){
47990         if(!this.regions[target]){
47991             var r = this.factory.create(target, this, config);
47992             this.bindRegion(target, r);
47993         }
47994         return this.regions[target];
47995     },
47996
47997     // private (kinda)
47998     bindRegion : function(name, r){
47999         this.regions[name] = r;
48000         r.on("visibilitychange", this.layout, this);
48001         r.on("paneladded", this.layout, this);
48002         r.on("panelremoved", this.layout, this);
48003         r.on("invalidated", this.layout, this);
48004         r.on("resized", this.onRegionResized, this);
48005         r.on("collapsed", this.onRegionCollapsed, this);
48006         r.on("expanded", this.onRegionExpanded, this);
48007     },
48008
48009     /**
48010      * Performs a layout update.
48011      */
48012     layout : function(){
48013         if(this.updating) return;
48014         var size = this.getViewSize();
48015         var w = size.width;
48016         var h = size.height;
48017         var centerW = w;
48018         var centerH = h;
48019         var centerY = 0;
48020         var centerX = 0;
48021         //var x = 0, y = 0;
48022
48023         var rs = this.regions;
48024         var north = rs["north"];
48025         var south = rs["south"]; 
48026         var west = rs["west"];
48027         var east = rs["east"];
48028         var center = rs["center"];
48029         //if(this.hideOnLayout){ // not supported anymore
48030             //c.el.setStyle("display", "none");
48031         //}
48032         if(north && north.isVisible()){
48033             var b = north.getBox();
48034             var m = north.getMargins();
48035             b.width = w - (m.left+m.right);
48036             b.x = m.left;
48037             b.y = m.top;
48038             centerY = b.height + b.y + m.bottom;
48039             centerH -= centerY;
48040             north.updateBox(this.safeBox(b));
48041         }
48042         if(south && south.isVisible()){
48043             var b = south.getBox();
48044             var m = south.getMargins();
48045             b.width = w - (m.left+m.right);
48046             b.x = m.left;
48047             var totalHeight = (b.height + m.top + m.bottom);
48048             b.y = h - totalHeight + m.top;
48049             centerH -= totalHeight;
48050             south.updateBox(this.safeBox(b));
48051         }
48052         if(west && west.isVisible()){
48053             var b = west.getBox();
48054             var m = west.getMargins();
48055             b.height = centerH - (m.top+m.bottom);
48056             b.x = m.left;
48057             b.y = centerY + m.top;
48058             var totalWidth = (b.width + m.left + m.right);
48059             centerX += totalWidth;
48060             centerW -= totalWidth;
48061             west.updateBox(this.safeBox(b));
48062         }
48063         if(east && east.isVisible()){
48064             var b = east.getBox();
48065             var m = east.getMargins();
48066             b.height = centerH - (m.top+m.bottom);
48067             var totalWidth = (b.width + m.left + m.right);
48068             b.x = w - totalWidth + m.left;
48069             b.y = centerY + m.top;
48070             centerW -= totalWidth;
48071             east.updateBox(this.safeBox(b));
48072         }
48073         if(center){
48074             var m = center.getMargins();
48075             var centerBox = {
48076                 x: centerX + m.left,
48077                 y: centerY + m.top,
48078                 width: centerW - (m.left+m.right),
48079                 height: centerH - (m.top+m.bottom)
48080             };
48081             //if(this.hideOnLayout){
48082                 //center.el.setStyle("display", "block");
48083             //}
48084             center.updateBox(this.safeBox(centerBox));
48085         }
48086         this.el.repaint();
48087         this.fireEvent("layout", this);
48088     },
48089
48090     // private
48091     safeBox : function(box){
48092         box.width = Math.max(0, box.width);
48093         box.height = Math.max(0, box.height);
48094         return box;
48095     },
48096
48097     /**
48098      * Adds a ContentPanel (or subclass) to this layout.
48099      * @param {String} target The target region key (north, south, east, west or center).
48100      * @param {Roo.ContentPanel} panel The panel to add
48101      * @return {Roo.ContentPanel} The added panel
48102      */
48103     add : function(target, panel){
48104          
48105         target = target.toLowerCase();
48106         return this.regions[target].add(panel);
48107     },
48108
48109     /**
48110      * Remove a ContentPanel (or subclass) to this layout.
48111      * @param {String} target The target region key (north, south, east, west or center).
48112      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48113      * @return {Roo.ContentPanel} The removed panel
48114      */
48115     remove : function(target, panel){
48116         target = target.toLowerCase();
48117         return this.regions[target].remove(panel);
48118     },
48119
48120     /**
48121      * Searches all regions for a panel with the specified id
48122      * @param {String} panelId
48123      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48124      */
48125     findPanel : function(panelId){
48126         var rs = this.regions;
48127         for(var target in rs){
48128             if(typeof rs[target] != "function"){
48129                 var p = rs[target].getPanel(panelId);
48130                 if(p){
48131                     return p;
48132                 }
48133             }
48134         }
48135         return null;
48136     },
48137
48138     /**
48139      * Searches all regions for a panel with the specified id and activates (shows) it.
48140      * @param {String/ContentPanel} panelId The panels id or the panel itself
48141      * @return {Roo.ContentPanel} The shown panel or null
48142      */
48143     showPanel : function(panelId) {
48144       var rs = this.regions;
48145       for(var target in rs){
48146          var r = rs[target];
48147          if(typeof r != "function"){
48148             if(r.hasPanel(panelId)){
48149                return r.showPanel(panelId);
48150             }
48151          }
48152       }
48153       return null;
48154    },
48155
48156    /**
48157      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48158      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48159      */
48160     restoreState : function(provider){
48161         if(!provider){
48162             provider = Roo.state.Manager;
48163         }
48164         var sm = new Roo.LayoutStateManager();
48165         sm.init(this, provider);
48166     },
48167
48168     /**
48169      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48170      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48171      * a valid ContentPanel config object.  Example:
48172      * <pre><code>
48173 // Create the main layout
48174 var layout = new Roo.BorderLayout('main-ct', {
48175     west: {
48176         split:true,
48177         minSize: 175,
48178         titlebar: true
48179     },
48180     center: {
48181         title:'Components'
48182     }
48183 }, 'main-ct');
48184
48185 // Create and add multiple ContentPanels at once via configs
48186 layout.batchAdd({
48187    west: {
48188        id: 'source-files',
48189        autoCreate:true,
48190        title:'Ext Source Files',
48191        autoScroll:true,
48192        fitToFrame:true
48193    },
48194    center : {
48195        el: cview,
48196        autoScroll:true,
48197        fitToFrame:true,
48198        toolbar: tb,
48199        resizeEl:'cbody'
48200    }
48201 });
48202 </code></pre>
48203      * @param {Object} regions An object containing ContentPanel configs by region name
48204      */
48205     batchAdd : function(regions){
48206         this.beginUpdate();
48207         for(var rname in regions){
48208             var lr = this.regions[rname];
48209             if(lr){
48210                 this.addTypedPanels(lr, regions[rname]);
48211             }
48212         }
48213         this.endUpdate();
48214     },
48215
48216     // private
48217     addTypedPanels : function(lr, ps){
48218         if(typeof ps == 'string'){
48219             lr.add(new Roo.ContentPanel(ps));
48220         }
48221         else if(ps instanceof Array){
48222             for(var i =0, len = ps.length; i < len; i++){
48223                 this.addTypedPanels(lr, ps[i]);
48224             }
48225         }
48226         else if(!ps.events){ // raw config?
48227             var el = ps.el;
48228             delete ps.el; // prevent conflict
48229             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48230         }
48231         else {  // panel object assumed!
48232             lr.add(ps);
48233         }
48234     },
48235     /**
48236      * Adds a xtype elements to the layout.
48237      * <pre><code>
48238
48239 layout.addxtype({
48240        xtype : 'ContentPanel',
48241        region: 'west',
48242        items: [ .... ]
48243    }
48244 );
48245
48246 layout.addxtype({
48247         xtype : 'NestedLayoutPanel',
48248         region: 'west',
48249         layout: {
48250            center: { },
48251            west: { }   
48252         },
48253         items : [ ... list of content panels or nested layout panels.. ]
48254    }
48255 );
48256 </code></pre>
48257      * @param {Object} cfg Xtype definition of item to add.
48258      */
48259     addxtype : function(cfg)
48260     {
48261         // basically accepts a pannel...
48262         // can accept a layout region..!?!?
48263         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48264         
48265         if (!cfg.xtype.match(/Panel$/)) {
48266             return false;
48267         }
48268         var ret = false;
48269         
48270         if (typeof(cfg.region) == 'undefined') {
48271             Roo.log("Failed to add Panel, region was not set");
48272             Roo.log(cfg);
48273             return false;
48274         }
48275         var region = cfg.region;
48276         delete cfg.region;
48277         
48278           
48279         var xitems = [];
48280         if (cfg.items) {
48281             xitems = cfg.items;
48282             delete cfg.items;
48283         }
48284         var nb = false;
48285         
48286         switch(cfg.xtype) 
48287         {
48288             case 'ContentPanel':  // ContentPanel (el, cfg)
48289             case 'ScrollPanel':  // ContentPanel (el, cfg)
48290             case 'ViewPanel': 
48291                 if(cfg.autoCreate) {
48292                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48293                 } else {
48294                     var el = this.el.createChild();
48295                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48296                 }
48297                 
48298                 this.add(region, ret);
48299                 break;
48300             
48301             
48302             case 'TreePanel': // our new panel!
48303                 cfg.el = this.el.createChild();
48304                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48305                 this.add(region, ret);
48306                 break;
48307             
48308             case 'NestedLayoutPanel': 
48309                 // create a new Layout (which is  a Border Layout...
48310                 var el = this.el.createChild();
48311                 var clayout = cfg.layout;
48312                 delete cfg.layout;
48313                 clayout.items   = clayout.items  || [];
48314                 // replace this exitems with the clayout ones..
48315                 xitems = clayout.items;
48316                  
48317                 
48318                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48319                     cfg.background = false;
48320                 }
48321                 var layout = new Roo.BorderLayout(el, clayout);
48322                 
48323                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48324                 //console.log('adding nested layout panel '  + cfg.toSource());
48325                 this.add(region, ret);
48326                 nb = {}; /// find first...
48327                 break;
48328                 
48329             case 'GridPanel': 
48330             
48331                 // needs grid and region
48332                 
48333                 //var el = this.getRegion(region).el.createChild();
48334                 var el = this.el.createChild();
48335                 // create the grid first...
48336                 
48337                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48338                 delete cfg.grid;
48339                 if (region == 'center' && this.active ) {
48340                     cfg.background = false;
48341                 }
48342                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48343                 
48344                 this.add(region, ret);
48345                 if (cfg.background) {
48346                     ret.on('activate', function(gp) {
48347                         if (!gp.grid.rendered) {
48348                             gp.grid.render();
48349                         }
48350                     });
48351                 } else {
48352                     grid.render();
48353                 }
48354                 break;
48355            
48356            
48357            
48358                 
48359                 
48360                 
48361             default: 
48362                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48363                 return null;
48364              // GridPanel (grid, cfg)
48365             
48366         }
48367         this.beginUpdate();
48368         // add children..
48369         var region = '';
48370         var abn = {};
48371         Roo.each(xitems, function(i)  {
48372             region = nb && i.region ? i.region : false;
48373             
48374             var add = ret.addxtype(i);
48375            
48376             if (region) {
48377                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48378                 if (!i.background) {
48379                     abn[region] = nb[region] ;
48380                 }
48381             }
48382             
48383         });
48384         this.endUpdate();
48385
48386         // make the last non-background panel active..
48387         //if (nb) { Roo.log(abn); }
48388         if (nb) {
48389             
48390             for(var r in abn) {
48391                 region = this.getRegion(r);
48392                 if (region) {
48393                     // tried using nb[r], but it does not work..
48394                      
48395                     region.showPanel(abn[r]);
48396                    
48397                 }
48398             }
48399         }
48400         return ret;
48401         
48402     }
48403 });
48404
48405 /**
48406  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48407  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48408  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48409  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48410  * <pre><code>
48411 // shorthand
48412 var CP = Roo.ContentPanel;
48413
48414 var layout = Roo.BorderLayout.create({
48415     north: {
48416         initialSize: 25,
48417         titlebar: false,
48418         panels: [new CP("north", "North")]
48419     },
48420     west: {
48421         split:true,
48422         initialSize: 200,
48423         minSize: 175,
48424         maxSize: 400,
48425         titlebar: true,
48426         collapsible: true,
48427         panels: [new CP("west", {title: "West"})]
48428     },
48429     east: {
48430         split:true,
48431         initialSize: 202,
48432         minSize: 175,
48433         maxSize: 400,
48434         titlebar: true,
48435         collapsible: true,
48436         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48437     },
48438     south: {
48439         split:true,
48440         initialSize: 100,
48441         minSize: 100,
48442         maxSize: 200,
48443         titlebar: true,
48444         collapsible: true,
48445         panels: [new CP("south", {title: "South", closable: true})]
48446     },
48447     center: {
48448         titlebar: true,
48449         autoScroll:true,
48450         resizeTabs: true,
48451         minTabWidth: 50,
48452         preferredTabWidth: 150,
48453         panels: [
48454             new CP("center1", {title: "Close Me", closable: true}),
48455             new CP("center2", {title: "Center Panel", closable: false})
48456         ]
48457     }
48458 }, document.body);
48459
48460 layout.getRegion("center").showPanel("center1");
48461 </code></pre>
48462  * @param config
48463  * @param targetEl
48464  */
48465 Roo.BorderLayout.create = function(config, targetEl){
48466     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48467     layout.beginUpdate();
48468     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48469     for(var j = 0, jlen = regions.length; j < jlen; j++){
48470         var lr = regions[j];
48471         if(layout.regions[lr] && config[lr].panels){
48472             var r = layout.regions[lr];
48473             var ps = config[lr].panels;
48474             layout.addTypedPanels(r, ps);
48475         }
48476     }
48477     layout.endUpdate();
48478     return layout;
48479 };
48480
48481 // private
48482 Roo.BorderLayout.RegionFactory = {
48483     // private
48484     validRegions : ["north","south","east","west","center"],
48485
48486     // private
48487     create : function(target, mgr, config){
48488         target = target.toLowerCase();
48489         if(config.lightweight || config.basic){
48490             return new Roo.BasicLayoutRegion(mgr, config, target);
48491         }
48492         switch(target){
48493             case "north":
48494                 return new Roo.NorthLayoutRegion(mgr, config);
48495             case "south":
48496                 return new Roo.SouthLayoutRegion(mgr, config);
48497             case "east":
48498                 return new Roo.EastLayoutRegion(mgr, config);
48499             case "west":
48500                 return new Roo.WestLayoutRegion(mgr, config);
48501             case "center":
48502                 return new Roo.CenterLayoutRegion(mgr, config);
48503         }
48504         throw 'Layout region "'+target+'" not supported.';
48505     }
48506 };/*
48507  * Based on:
48508  * Ext JS Library 1.1.1
48509  * Copyright(c) 2006-2007, Ext JS, LLC.
48510  *
48511  * Originally Released Under LGPL - original licence link has changed is not relivant.
48512  *
48513  * Fork - LGPL
48514  * <script type="text/javascript">
48515  */
48516  
48517 /**
48518  * @class Roo.BasicLayoutRegion
48519  * @extends Roo.util.Observable
48520  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48521  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48522  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48523  */
48524 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48525     this.mgr = mgr;
48526     this.position  = pos;
48527     this.events = {
48528         /**
48529          * @scope Roo.BasicLayoutRegion
48530          */
48531         
48532         /**
48533          * @event beforeremove
48534          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48535          * @param {Roo.LayoutRegion} this
48536          * @param {Roo.ContentPanel} panel The panel
48537          * @param {Object} e The cancel event object
48538          */
48539         "beforeremove" : true,
48540         /**
48541          * @event invalidated
48542          * Fires when the layout for this region is changed.
48543          * @param {Roo.LayoutRegion} this
48544          */
48545         "invalidated" : true,
48546         /**
48547          * @event visibilitychange
48548          * Fires when this region is shown or hidden 
48549          * @param {Roo.LayoutRegion} this
48550          * @param {Boolean} visibility true or false
48551          */
48552         "visibilitychange" : true,
48553         /**
48554          * @event paneladded
48555          * Fires when a panel is added. 
48556          * @param {Roo.LayoutRegion} this
48557          * @param {Roo.ContentPanel} panel The panel
48558          */
48559         "paneladded" : true,
48560         /**
48561          * @event panelremoved
48562          * Fires when a panel is removed. 
48563          * @param {Roo.LayoutRegion} this
48564          * @param {Roo.ContentPanel} panel The panel
48565          */
48566         "panelremoved" : true,
48567         /**
48568          * @event collapsed
48569          * Fires when this region is collapsed.
48570          * @param {Roo.LayoutRegion} this
48571          */
48572         "collapsed" : true,
48573         /**
48574          * @event expanded
48575          * Fires when this region is expanded.
48576          * @param {Roo.LayoutRegion} this
48577          */
48578         "expanded" : true,
48579         /**
48580          * @event slideshow
48581          * Fires when this region is slid into view.
48582          * @param {Roo.LayoutRegion} this
48583          */
48584         "slideshow" : true,
48585         /**
48586          * @event slidehide
48587          * Fires when this region slides out of view. 
48588          * @param {Roo.LayoutRegion} this
48589          */
48590         "slidehide" : true,
48591         /**
48592          * @event panelactivated
48593          * Fires when a panel is activated. 
48594          * @param {Roo.LayoutRegion} this
48595          * @param {Roo.ContentPanel} panel The activated panel
48596          */
48597         "panelactivated" : true,
48598         /**
48599          * @event resized
48600          * Fires when the user resizes this region. 
48601          * @param {Roo.LayoutRegion} this
48602          * @param {Number} newSize The new size (width for east/west, height for north/south)
48603          */
48604         "resized" : true
48605     };
48606     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48607     this.panels = new Roo.util.MixedCollection();
48608     this.panels.getKey = this.getPanelId.createDelegate(this);
48609     this.box = null;
48610     this.activePanel = null;
48611     // ensure listeners are added...
48612     
48613     if (config.listeners || config.events) {
48614         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48615             listeners : config.listeners || {},
48616             events : config.events || {}
48617         });
48618     }
48619     
48620     if(skipConfig !== true){
48621         this.applyConfig(config);
48622     }
48623 };
48624
48625 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48626     getPanelId : function(p){
48627         return p.getId();
48628     },
48629     
48630     applyConfig : function(config){
48631         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48632         this.config = config;
48633         
48634     },
48635     
48636     /**
48637      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48638      * the width, for horizontal (north, south) the height.
48639      * @param {Number} newSize The new width or height
48640      */
48641     resizeTo : function(newSize){
48642         var el = this.el ? this.el :
48643                  (this.activePanel ? this.activePanel.getEl() : null);
48644         if(el){
48645             switch(this.position){
48646                 case "east":
48647                 case "west":
48648                     el.setWidth(newSize);
48649                     this.fireEvent("resized", this, newSize);
48650                 break;
48651                 case "north":
48652                 case "south":
48653                     el.setHeight(newSize);
48654                     this.fireEvent("resized", this, newSize);
48655                 break;                
48656             }
48657         }
48658     },
48659     
48660     getBox : function(){
48661         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48662     },
48663     
48664     getMargins : function(){
48665         return this.margins;
48666     },
48667     
48668     updateBox : function(box){
48669         this.box = box;
48670         var el = this.activePanel.getEl();
48671         el.dom.style.left = box.x + "px";
48672         el.dom.style.top = box.y + "px";
48673         this.activePanel.setSize(box.width, box.height);
48674     },
48675     
48676     /**
48677      * Returns the container element for this region.
48678      * @return {Roo.Element}
48679      */
48680     getEl : function(){
48681         return this.activePanel;
48682     },
48683     
48684     /**
48685      * Returns true if this region is currently visible.
48686      * @return {Boolean}
48687      */
48688     isVisible : function(){
48689         return this.activePanel ? true : false;
48690     },
48691     
48692     setActivePanel : function(panel){
48693         panel = this.getPanel(panel);
48694         if(this.activePanel && this.activePanel != panel){
48695             this.activePanel.setActiveState(false);
48696             this.activePanel.getEl().setLeftTop(-10000,-10000);
48697         }
48698         this.activePanel = panel;
48699         panel.setActiveState(true);
48700         if(this.box){
48701             panel.setSize(this.box.width, this.box.height);
48702         }
48703         this.fireEvent("panelactivated", this, panel);
48704         this.fireEvent("invalidated");
48705     },
48706     
48707     /**
48708      * Show the specified panel.
48709      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48710      * @return {Roo.ContentPanel} The shown panel or null
48711      */
48712     showPanel : function(panel){
48713         if(panel = this.getPanel(panel)){
48714             this.setActivePanel(panel);
48715         }
48716         return panel;
48717     },
48718     
48719     /**
48720      * Get the active panel for this region.
48721      * @return {Roo.ContentPanel} The active panel or null
48722      */
48723     getActivePanel : function(){
48724         return this.activePanel;
48725     },
48726     
48727     /**
48728      * Add the passed ContentPanel(s)
48729      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48730      * @return {Roo.ContentPanel} The panel added (if only one was added)
48731      */
48732     add : function(panel){
48733         if(arguments.length > 1){
48734             for(var i = 0, len = arguments.length; i < len; i++) {
48735                 this.add(arguments[i]);
48736             }
48737             return null;
48738         }
48739         if(this.hasPanel(panel)){
48740             this.showPanel(panel);
48741             return panel;
48742         }
48743         var el = panel.getEl();
48744         if(el.dom.parentNode != this.mgr.el.dom){
48745             this.mgr.el.dom.appendChild(el.dom);
48746         }
48747         if(panel.setRegion){
48748             panel.setRegion(this);
48749         }
48750         this.panels.add(panel);
48751         el.setStyle("position", "absolute");
48752         if(!panel.background){
48753             this.setActivePanel(panel);
48754             if(this.config.initialSize && this.panels.getCount()==1){
48755                 this.resizeTo(this.config.initialSize);
48756             }
48757         }
48758         this.fireEvent("paneladded", this, panel);
48759         return panel;
48760     },
48761     
48762     /**
48763      * Returns true if the panel is in this region.
48764      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48765      * @return {Boolean}
48766      */
48767     hasPanel : function(panel){
48768         if(typeof panel == "object"){ // must be panel obj
48769             panel = panel.getId();
48770         }
48771         return this.getPanel(panel) ? true : false;
48772     },
48773     
48774     /**
48775      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48776      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48777      * @param {Boolean} preservePanel Overrides the config preservePanel option
48778      * @return {Roo.ContentPanel} The panel that was removed
48779      */
48780     remove : function(panel, preservePanel){
48781         panel = this.getPanel(panel);
48782         if(!panel){
48783             return null;
48784         }
48785         var e = {};
48786         this.fireEvent("beforeremove", this, panel, e);
48787         if(e.cancel === true){
48788             return null;
48789         }
48790         var panelId = panel.getId();
48791         this.panels.removeKey(panelId);
48792         return panel;
48793     },
48794     
48795     /**
48796      * Returns the panel specified or null if it's not in this region.
48797      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48798      * @return {Roo.ContentPanel}
48799      */
48800     getPanel : function(id){
48801         if(typeof id == "object"){ // must be panel obj
48802             return id;
48803         }
48804         return this.panels.get(id);
48805     },
48806     
48807     /**
48808      * Returns this regions position (north/south/east/west/center).
48809      * @return {String} 
48810      */
48811     getPosition: function(){
48812         return this.position;    
48813     }
48814 });/*
48815  * Based on:
48816  * Ext JS Library 1.1.1
48817  * Copyright(c) 2006-2007, Ext JS, LLC.
48818  *
48819  * Originally Released Under LGPL - original licence link has changed is not relivant.
48820  *
48821  * Fork - LGPL
48822  * <script type="text/javascript">
48823  */
48824  
48825 /**
48826  * @class Roo.LayoutRegion
48827  * @extends Roo.BasicLayoutRegion
48828  * This class represents a region in a layout manager.
48829  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48830  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48831  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48832  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48833  * @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})
48834  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48835  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48836  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48837  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48838  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48839  * @cfg {String}    title           The title for the region (overrides panel titles)
48840  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48841  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48842  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48843  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48844  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48845  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48846  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48847  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48848  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48849  * @cfg {Boolean}   showPin         True to show a pin button
48850  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48851  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48852  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48853  * @cfg {Number}    width           For East/West panels
48854  * @cfg {Number}    height          For North/South panels
48855  * @cfg {Boolean}   split           To show the splitter
48856  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48857  */
48858 Roo.LayoutRegion = function(mgr, config, pos){
48859     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48860     var dh = Roo.DomHelper;
48861     /** This region's container element 
48862     * @type Roo.Element */
48863     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48864     /** This region's title element 
48865     * @type Roo.Element */
48866
48867     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48868         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48869         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48870     ]}, true);
48871     this.titleEl.enableDisplayMode();
48872     /** This region's title text element 
48873     * @type HTMLElement */
48874     this.titleTextEl = this.titleEl.dom.firstChild;
48875     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48876     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48877     this.closeBtn.enableDisplayMode();
48878     this.closeBtn.on("click", this.closeClicked, this);
48879     this.closeBtn.hide();
48880
48881     this.createBody(config);
48882     this.visible = true;
48883     this.collapsed = false;
48884
48885     if(config.hideWhenEmpty){
48886         this.hide();
48887         this.on("paneladded", this.validateVisibility, this);
48888         this.on("panelremoved", this.validateVisibility, this);
48889     }
48890     this.applyConfig(config);
48891 };
48892
48893 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48894
48895     createBody : function(){
48896         /** This region's body element 
48897         * @type Roo.Element */
48898         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48899     },
48900
48901     applyConfig : function(c){
48902         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48903             var dh = Roo.DomHelper;
48904             if(c.titlebar !== false){
48905                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48906                 this.collapseBtn.on("click", this.collapse, this);
48907                 this.collapseBtn.enableDisplayMode();
48908
48909                 if(c.showPin === true || this.showPin){
48910                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48911                     this.stickBtn.enableDisplayMode();
48912                     this.stickBtn.on("click", this.expand, this);
48913                     this.stickBtn.hide();
48914                 }
48915             }
48916             /** This region's collapsed element
48917             * @type Roo.Element */
48918             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48919                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48920             ]}, true);
48921             if(c.floatable !== false){
48922                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48923                this.collapsedEl.on("click", this.collapseClick, this);
48924             }
48925
48926             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48927                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48928                    id: "message", unselectable: "on", style:{"float":"left"}});
48929                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48930              }
48931             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48932             this.expandBtn.on("click", this.expand, this);
48933         }
48934         if(this.collapseBtn){
48935             this.collapseBtn.setVisible(c.collapsible == true);
48936         }
48937         this.cmargins = c.cmargins || this.cmargins ||
48938                          (this.position == "west" || this.position == "east" ?
48939                              {top: 0, left: 2, right:2, bottom: 0} :
48940                              {top: 2, left: 0, right:0, bottom: 2});
48941         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48942         this.bottomTabs = c.tabPosition != "top";
48943         this.autoScroll = c.autoScroll || false;
48944         if(this.autoScroll){
48945             this.bodyEl.setStyle("overflow", "auto");
48946         }else{
48947             this.bodyEl.setStyle("overflow", "hidden");
48948         }
48949         //if(c.titlebar !== false){
48950             if((!c.titlebar && !c.title) || c.titlebar === false){
48951                 this.titleEl.hide();
48952             }else{
48953                 this.titleEl.show();
48954                 if(c.title){
48955                     this.titleTextEl.innerHTML = c.title;
48956                 }
48957             }
48958         //}
48959         this.duration = c.duration || .30;
48960         this.slideDuration = c.slideDuration || .45;
48961         this.config = c;
48962         if(c.collapsed){
48963             this.collapse(true);
48964         }
48965         if(c.hidden){
48966             this.hide();
48967         }
48968     },
48969     /**
48970      * Returns true if this region is currently visible.
48971      * @return {Boolean}
48972      */
48973     isVisible : function(){
48974         return this.visible;
48975     },
48976
48977     /**
48978      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
48979      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
48980      */
48981     setCollapsedTitle : function(title){
48982         title = title || "&#160;";
48983         if(this.collapsedTitleTextEl){
48984             this.collapsedTitleTextEl.innerHTML = title;
48985         }
48986     },
48987
48988     getBox : function(){
48989         var b;
48990         if(!this.collapsed){
48991             b = this.el.getBox(false, true);
48992         }else{
48993             b = this.collapsedEl.getBox(false, true);
48994         }
48995         return b;
48996     },
48997
48998     getMargins : function(){
48999         return this.collapsed ? this.cmargins : this.margins;
49000     },
49001
49002     highlight : function(){
49003         this.el.addClass("x-layout-panel-dragover");
49004     },
49005
49006     unhighlight : function(){
49007         this.el.removeClass("x-layout-panel-dragover");
49008     },
49009
49010     updateBox : function(box){
49011         this.box = box;
49012         if(!this.collapsed){
49013             this.el.dom.style.left = box.x + "px";
49014             this.el.dom.style.top = box.y + "px";
49015             this.updateBody(box.width, box.height);
49016         }else{
49017             this.collapsedEl.dom.style.left = box.x + "px";
49018             this.collapsedEl.dom.style.top = box.y + "px";
49019             this.collapsedEl.setSize(box.width, box.height);
49020         }
49021         if(this.tabs){
49022             this.tabs.autoSizeTabs();
49023         }
49024     },
49025
49026     updateBody : function(w, h){
49027         if(w !== null){
49028             this.el.setWidth(w);
49029             w -= this.el.getBorderWidth("rl");
49030             if(this.config.adjustments){
49031                 w += this.config.adjustments[0];
49032             }
49033         }
49034         if(h !== null){
49035             this.el.setHeight(h);
49036             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49037             h -= this.el.getBorderWidth("tb");
49038             if(this.config.adjustments){
49039                 h += this.config.adjustments[1];
49040             }
49041             this.bodyEl.setHeight(h);
49042             if(this.tabs){
49043                 h = this.tabs.syncHeight(h);
49044             }
49045         }
49046         if(this.panelSize){
49047             w = w !== null ? w : this.panelSize.width;
49048             h = h !== null ? h : this.panelSize.height;
49049         }
49050         if(this.activePanel){
49051             var el = this.activePanel.getEl();
49052             w = w !== null ? w : el.getWidth();
49053             h = h !== null ? h : el.getHeight();
49054             this.panelSize = {width: w, height: h};
49055             this.activePanel.setSize(w, h);
49056         }
49057         if(Roo.isIE && this.tabs){
49058             this.tabs.el.repaint();
49059         }
49060     },
49061
49062     /**
49063      * Returns the container element for this region.
49064      * @return {Roo.Element}
49065      */
49066     getEl : function(){
49067         return this.el;
49068     },
49069
49070     /**
49071      * Hides this region.
49072      */
49073     hide : function(){
49074         if(!this.collapsed){
49075             this.el.dom.style.left = "-2000px";
49076             this.el.hide();
49077         }else{
49078             this.collapsedEl.dom.style.left = "-2000px";
49079             this.collapsedEl.hide();
49080         }
49081         this.visible = false;
49082         this.fireEvent("visibilitychange", this, false);
49083     },
49084
49085     /**
49086      * Shows this region if it was previously hidden.
49087      */
49088     show : function(){
49089         if(!this.collapsed){
49090             this.el.show();
49091         }else{
49092             this.collapsedEl.show();
49093         }
49094         this.visible = true;
49095         this.fireEvent("visibilitychange", this, true);
49096     },
49097
49098     closeClicked : function(){
49099         if(this.activePanel){
49100             this.remove(this.activePanel);
49101         }
49102     },
49103
49104     collapseClick : function(e){
49105         if(this.isSlid){
49106            e.stopPropagation();
49107            this.slideIn();
49108         }else{
49109            e.stopPropagation();
49110            this.slideOut();
49111         }
49112     },
49113
49114     /**
49115      * Collapses this region.
49116      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49117      */
49118     collapse : function(skipAnim){
49119         if(this.collapsed) return;
49120         this.collapsed = true;
49121         if(this.split){
49122             this.split.el.hide();
49123         }
49124         if(this.config.animate && skipAnim !== true){
49125             this.fireEvent("invalidated", this);
49126             this.animateCollapse();
49127         }else{
49128             this.el.setLocation(-20000,-20000);
49129             this.el.hide();
49130             this.collapsedEl.show();
49131             this.fireEvent("collapsed", this);
49132             this.fireEvent("invalidated", this);
49133         }
49134     },
49135
49136     animateCollapse : function(){
49137         // overridden
49138     },
49139
49140     /**
49141      * Expands this region if it was previously collapsed.
49142      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49143      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49144      */
49145     expand : function(e, skipAnim){
49146         if(e) e.stopPropagation();
49147         if(!this.collapsed || this.el.hasActiveFx()) return;
49148         if(this.isSlid){
49149             this.afterSlideIn();
49150             skipAnim = true;
49151         }
49152         this.collapsed = false;
49153         if(this.config.animate && skipAnim !== true){
49154             this.animateExpand();
49155         }else{
49156             this.el.show();
49157             if(this.split){
49158                 this.split.el.show();
49159             }
49160             this.collapsedEl.setLocation(-2000,-2000);
49161             this.collapsedEl.hide();
49162             this.fireEvent("invalidated", this);
49163             this.fireEvent("expanded", this);
49164         }
49165     },
49166
49167     animateExpand : function(){
49168         // overridden
49169     },
49170
49171     initTabs : function()
49172     {
49173         this.bodyEl.setStyle("overflow", "hidden");
49174         var ts = new Roo.TabPanel(
49175                 this.bodyEl.dom,
49176                 {
49177                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49178                     disableTooltips: this.config.disableTabTips,
49179                     toolbar : this.config.toolbar
49180                 }
49181         );
49182         if(this.config.hideTabs){
49183             ts.stripWrap.setDisplayed(false);
49184         }
49185         this.tabs = ts;
49186         ts.resizeTabs = this.config.resizeTabs === true;
49187         ts.minTabWidth = this.config.minTabWidth || 40;
49188         ts.maxTabWidth = this.config.maxTabWidth || 250;
49189         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49190         ts.monitorResize = false;
49191         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49192         ts.bodyEl.addClass('x-layout-tabs-body');
49193         this.panels.each(this.initPanelAsTab, this);
49194     },
49195
49196     initPanelAsTab : function(panel){
49197         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49198                     this.config.closeOnTab && panel.isClosable());
49199         if(panel.tabTip !== undefined){
49200             ti.setTooltip(panel.tabTip);
49201         }
49202         ti.on("activate", function(){
49203               this.setActivePanel(panel);
49204         }, this);
49205         if(this.config.closeOnTab){
49206             ti.on("beforeclose", function(t, e){
49207                 e.cancel = true;
49208                 this.remove(panel);
49209             }, this);
49210         }
49211         return ti;
49212     },
49213
49214     updatePanelTitle : function(panel, title){
49215         if(this.activePanel == panel){
49216             this.updateTitle(title);
49217         }
49218         if(this.tabs){
49219             var ti = this.tabs.getTab(panel.getEl().id);
49220             ti.setText(title);
49221             if(panel.tabTip !== undefined){
49222                 ti.setTooltip(panel.tabTip);
49223             }
49224         }
49225     },
49226
49227     updateTitle : function(title){
49228         if(this.titleTextEl && !this.config.title){
49229             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49230         }
49231     },
49232
49233     setActivePanel : function(panel){
49234         panel = this.getPanel(panel);
49235         if(this.activePanel && this.activePanel != panel){
49236             this.activePanel.setActiveState(false);
49237         }
49238         this.activePanel = panel;
49239         panel.setActiveState(true);
49240         if(this.panelSize){
49241             panel.setSize(this.panelSize.width, this.panelSize.height);
49242         }
49243         if(this.closeBtn){
49244             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49245         }
49246         this.updateTitle(panel.getTitle());
49247         if(this.tabs){
49248             this.fireEvent("invalidated", this);
49249         }
49250         this.fireEvent("panelactivated", this, panel);
49251     },
49252
49253     /**
49254      * Shows the specified panel.
49255      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49256      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49257      */
49258     showPanel : function(panel){
49259         if(panel = this.getPanel(panel)){
49260             if(this.tabs){
49261                 var tab = this.tabs.getTab(panel.getEl().id);
49262                 if(tab.isHidden()){
49263                     this.tabs.unhideTab(tab.id);
49264                 }
49265                 tab.activate();
49266             }else{
49267                 this.setActivePanel(panel);
49268             }
49269         }
49270         return panel;
49271     },
49272
49273     /**
49274      * Get the active panel for this region.
49275      * @return {Roo.ContentPanel} The active panel or null
49276      */
49277     getActivePanel : function(){
49278         return this.activePanel;
49279     },
49280
49281     validateVisibility : function(){
49282         if(this.panels.getCount() < 1){
49283             this.updateTitle("&#160;");
49284             this.closeBtn.hide();
49285             this.hide();
49286         }else{
49287             if(!this.isVisible()){
49288                 this.show();
49289             }
49290         }
49291     },
49292
49293     /**
49294      * Adds the passed ContentPanel(s) to this region.
49295      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49296      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49297      */
49298     add : function(panel){
49299         if(arguments.length > 1){
49300             for(var i = 0, len = arguments.length; i < len; i++) {
49301                 this.add(arguments[i]);
49302             }
49303             return null;
49304         }
49305         if(this.hasPanel(panel)){
49306             this.showPanel(panel);
49307             return panel;
49308         }
49309         panel.setRegion(this);
49310         this.panels.add(panel);
49311         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49312             this.bodyEl.dom.appendChild(panel.getEl().dom);
49313             if(panel.background !== true){
49314                 this.setActivePanel(panel);
49315             }
49316             this.fireEvent("paneladded", this, panel);
49317             return panel;
49318         }
49319         if(!this.tabs){
49320             this.initTabs();
49321         }else{
49322             this.initPanelAsTab(panel);
49323         }
49324         if(panel.background !== true){
49325             this.tabs.activate(panel.getEl().id);
49326         }
49327         this.fireEvent("paneladded", this, panel);
49328         return panel;
49329     },
49330
49331     /**
49332      * Hides the tab for the specified panel.
49333      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49334      */
49335     hidePanel : function(panel){
49336         if(this.tabs && (panel = this.getPanel(panel))){
49337             this.tabs.hideTab(panel.getEl().id);
49338         }
49339     },
49340
49341     /**
49342      * Unhides the tab for a previously hidden panel.
49343      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49344      */
49345     unhidePanel : function(panel){
49346         if(this.tabs && (panel = this.getPanel(panel))){
49347             this.tabs.unhideTab(panel.getEl().id);
49348         }
49349     },
49350
49351     clearPanels : function(){
49352         while(this.panels.getCount() > 0){
49353              this.remove(this.panels.first());
49354         }
49355     },
49356
49357     /**
49358      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49359      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49360      * @param {Boolean} preservePanel Overrides the config preservePanel option
49361      * @return {Roo.ContentPanel} The panel that was removed
49362      */
49363     remove : function(panel, preservePanel){
49364         panel = this.getPanel(panel);
49365         if(!panel){
49366             return null;
49367         }
49368         var e = {};
49369         this.fireEvent("beforeremove", this, panel, e);
49370         if(e.cancel === true){
49371             return null;
49372         }
49373         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49374         var panelId = panel.getId();
49375         this.panels.removeKey(panelId);
49376         if(preservePanel){
49377             document.body.appendChild(panel.getEl().dom);
49378         }
49379         if(this.tabs){
49380             this.tabs.removeTab(panel.getEl().id);
49381         }else if (!preservePanel){
49382             this.bodyEl.dom.removeChild(panel.getEl().dom);
49383         }
49384         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49385             var p = this.panels.first();
49386             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49387             tempEl.appendChild(p.getEl().dom);
49388             this.bodyEl.update("");
49389             this.bodyEl.dom.appendChild(p.getEl().dom);
49390             tempEl = null;
49391             this.updateTitle(p.getTitle());
49392             this.tabs = null;
49393             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49394             this.setActivePanel(p);
49395         }
49396         panel.setRegion(null);
49397         if(this.activePanel == panel){
49398             this.activePanel = null;
49399         }
49400         if(this.config.autoDestroy !== false && preservePanel !== true){
49401             try{panel.destroy();}catch(e){}
49402         }
49403         this.fireEvent("panelremoved", this, panel);
49404         return panel;
49405     },
49406
49407     /**
49408      * Returns the TabPanel component used by this region
49409      * @return {Roo.TabPanel}
49410      */
49411     getTabs : function(){
49412         return this.tabs;
49413     },
49414
49415     createTool : function(parentEl, className){
49416         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49417             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49418         btn.addClassOnOver("x-layout-tools-button-over");
49419         return btn;
49420     }
49421 });/*
49422  * Based on:
49423  * Ext JS Library 1.1.1
49424  * Copyright(c) 2006-2007, Ext JS, LLC.
49425  *
49426  * Originally Released Under LGPL - original licence link has changed is not relivant.
49427  *
49428  * Fork - LGPL
49429  * <script type="text/javascript">
49430  */
49431  
49432
49433
49434 /**
49435  * @class Roo.SplitLayoutRegion
49436  * @extends Roo.LayoutRegion
49437  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49438  */
49439 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49440     this.cursor = cursor;
49441     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49442 };
49443
49444 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49445     splitTip : "Drag to resize.",
49446     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49447     useSplitTips : false,
49448
49449     applyConfig : function(config){
49450         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49451         if(config.split){
49452             if(!this.split){
49453                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49454                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49455                 /** The SplitBar for this region 
49456                 * @type Roo.SplitBar */
49457                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49458                 this.split.on("moved", this.onSplitMove, this);
49459                 this.split.useShim = config.useShim === true;
49460                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49461                 if(this.useSplitTips){
49462                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49463                 }
49464                 if(config.collapsible){
49465                     this.split.el.on("dblclick", this.collapse,  this);
49466                 }
49467             }
49468             if(typeof config.minSize != "undefined"){
49469                 this.split.minSize = config.minSize;
49470             }
49471             if(typeof config.maxSize != "undefined"){
49472                 this.split.maxSize = config.maxSize;
49473             }
49474             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49475                 this.hideSplitter();
49476             }
49477         }
49478     },
49479
49480     getHMaxSize : function(){
49481          var cmax = this.config.maxSize || 10000;
49482          var center = this.mgr.getRegion("center");
49483          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49484     },
49485
49486     getVMaxSize : function(){
49487          var cmax = this.config.maxSize || 10000;
49488          var center = this.mgr.getRegion("center");
49489          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49490     },
49491
49492     onSplitMove : function(split, newSize){
49493         this.fireEvent("resized", this, newSize);
49494     },
49495     
49496     /** 
49497      * Returns the {@link Roo.SplitBar} for this region.
49498      * @return {Roo.SplitBar}
49499      */
49500     getSplitBar : function(){
49501         return this.split;
49502     },
49503     
49504     hide : function(){
49505         this.hideSplitter();
49506         Roo.SplitLayoutRegion.superclass.hide.call(this);
49507     },
49508
49509     hideSplitter : function(){
49510         if(this.split){
49511             this.split.el.setLocation(-2000,-2000);
49512             this.split.el.hide();
49513         }
49514     },
49515
49516     show : function(){
49517         if(this.split){
49518             this.split.el.show();
49519         }
49520         Roo.SplitLayoutRegion.superclass.show.call(this);
49521     },
49522     
49523     beforeSlide: function(){
49524         if(Roo.isGecko){// firefox overflow auto bug workaround
49525             this.bodyEl.clip();
49526             if(this.tabs) this.tabs.bodyEl.clip();
49527             if(this.activePanel){
49528                 this.activePanel.getEl().clip();
49529                 
49530                 if(this.activePanel.beforeSlide){
49531                     this.activePanel.beforeSlide();
49532                 }
49533             }
49534         }
49535     },
49536     
49537     afterSlide : function(){
49538         if(Roo.isGecko){// firefox overflow auto bug workaround
49539             this.bodyEl.unclip();
49540             if(this.tabs) this.tabs.bodyEl.unclip();
49541             if(this.activePanel){
49542                 this.activePanel.getEl().unclip();
49543                 if(this.activePanel.afterSlide){
49544                     this.activePanel.afterSlide();
49545                 }
49546             }
49547         }
49548     },
49549
49550     initAutoHide : function(){
49551         if(this.autoHide !== false){
49552             if(!this.autoHideHd){
49553                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49554                 this.autoHideHd = {
49555                     "mouseout": function(e){
49556                         if(!e.within(this.el, true)){
49557                             st.delay(500);
49558                         }
49559                     },
49560                     "mouseover" : function(e){
49561                         st.cancel();
49562                     },
49563                     scope : this
49564                 };
49565             }
49566             this.el.on(this.autoHideHd);
49567         }
49568     },
49569
49570     clearAutoHide : function(){
49571         if(this.autoHide !== false){
49572             this.el.un("mouseout", this.autoHideHd.mouseout);
49573             this.el.un("mouseover", this.autoHideHd.mouseover);
49574         }
49575     },
49576
49577     clearMonitor : function(){
49578         Roo.get(document).un("click", this.slideInIf, this);
49579     },
49580
49581     // these names are backwards but not changed for compat
49582     slideOut : function(){
49583         if(this.isSlid || this.el.hasActiveFx()){
49584             return;
49585         }
49586         this.isSlid = true;
49587         if(this.collapseBtn){
49588             this.collapseBtn.hide();
49589         }
49590         this.closeBtnState = this.closeBtn.getStyle('display');
49591         this.closeBtn.hide();
49592         if(this.stickBtn){
49593             this.stickBtn.show();
49594         }
49595         this.el.show();
49596         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49597         this.beforeSlide();
49598         this.el.setStyle("z-index", 10001);
49599         this.el.slideIn(this.getSlideAnchor(), {
49600             callback: function(){
49601                 this.afterSlide();
49602                 this.initAutoHide();
49603                 Roo.get(document).on("click", this.slideInIf, this);
49604                 this.fireEvent("slideshow", this);
49605             },
49606             scope: this,
49607             block: true
49608         });
49609     },
49610
49611     afterSlideIn : function(){
49612         this.clearAutoHide();
49613         this.isSlid = false;
49614         this.clearMonitor();
49615         this.el.setStyle("z-index", "");
49616         if(this.collapseBtn){
49617             this.collapseBtn.show();
49618         }
49619         this.closeBtn.setStyle('display', this.closeBtnState);
49620         if(this.stickBtn){
49621             this.stickBtn.hide();
49622         }
49623         this.fireEvent("slidehide", this);
49624     },
49625
49626     slideIn : function(cb){
49627         if(!this.isSlid || this.el.hasActiveFx()){
49628             Roo.callback(cb);
49629             return;
49630         }
49631         this.isSlid = false;
49632         this.beforeSlide();
49633         this.el.slideOut(this.getSlideAnchor(), {
49634             callback: function(){
49635                 this.el.setLeftTop(-10000, -10000);
49636                 this.afterSlide();
49637                 this.afterSlideIn();
49638                 Roo.callback(cb);
49639             },
49640             scope: this,
49641             block: true
49642         });
49643     },
49644     
49645     slideInIf : function(e){
49646         if(!e.within(this.el)){
49647             this.slideIn();
49648         }
49649     },
49650
49651     animateCollapse : function(){
49652         this.beforeSlide();
49653         this.el.setStyle("z-index", 20000);
49654         var anchor = this.getSlideAnchor();
49655         this.el.slideOut(anchor, {
49656             callback : function(){
49657                 this.el.setStyle("z-index", "");
49658                 this.collapsedEl.slideIn(anchor, {duration:.3});
49659                 this.afterSlide();
49660                 this.el.setLocation(-10000,-10000);
49661                 this.el.hide();
49662                 this.fireEvent("collapsed", this);
49663             },
49664             scope: this,
49665             block: true
49666         });
49667     },
49668
49669     animateExpand : function(){
49670         this.beforeSlide();
49671         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49672         this.el.setStyle("z-index", 20000);
49673         this.collapsedEl.hide({
49674             duration:.1
49675         });
49676         this.el.slideIn(this.getSlideAnchor(), {
49677             callback : function(){
49678                 this.el.setStyle("z-index", "");
49679                 this.afterSlide();
49680                 if(this.split){
49681                     this.split.el.show();
49682                 }
49683                 this.fireEvent("invalidated", this);
49684                 this.fireEvent("expanded", this);
49685             },
49686             scope: this,
49687             block: true
49688         });
49689     },
49690
49691     anchors : {
49692         "west" : "left",
49693         "east" : "right",
49694         "north" : "top",
49695         "south" : "bottom"
49696     },
49697
49698     sanchors : {
49699         "west" : "l",
49700         "east" : "r",
49701         "north" : "t",
49702         "south" : "b"
49703     },
49704
49705     canchors : {
49706         "west" : "tl-tr",
49707         "east" : "tr-tl",
49708         "north" : "tl-bl",
49709         "south" : "bl-tl"
49710     },
49711
49712     getAnchor : function(){
49713         return this.anchors[this.position];
49714     },
49715
49716     getCollapseAnchor : function(){
49717         return this.canchors[this.position];
49718     },
49719
49720     getSlideAnchor : function(){
49721         return this.sanchors[this.position];
49722     },
49723
49724     getAlignAdj : function(){
49725         var cm = this.cmargins;
49726         switch(this.position){
49727             case "west":
49728                 return [0, 0];
49729             break;
49730             case "east":
49731                 return [0, 0];
49732             break;
49733             case "north":
49734                 return [0, 0];
49735             break;
49736             case "south":
49737                 return [0, 0];
49738             break;
49739         }
49740     },
49741
49742     getExpandAdj : function(){
49743         var c = this.collapsedEl, cm = this.cmargins;
49744         switch(this.position){
49745             case "west":
49746                 return [-(cm.right+c.getWidth()+cm.left), 0];
49747             break;
49748             case "east":
49749                 return [cm.right+c.getWidth()+cm.left, 0];
49750             break;
49751             case "north":
49752                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49753             break;
49754             case "south":
49755                 return [0, cm.top+cm.bottom+c.getHeight()];
49756             break;
49757         }
49758     }
49759 });/*
49760  * Based on:
49761  * Ext JS Library 1.1.1
49762  * Copyright(c) 2006-2007, Ext JS, LLC.
49763  *
49764  * Originally Released Under LGPL - original licence link has changed is not relivant.
49765  *
49766  * Fork - LGPL
49767  * <script type="text/javascript">
49768  */
49769 /*
49770  * These classes are private internal classes
49771  */
49772 Roo.CenterLayoutRegion = function(mgr, config){
49773     Roo.LayoutRegion.call(this, mgr, config, "center");
49774     this.visible = true;
49775     this.minWidth = config.minWidth || 20;
49776     this.minHeight = config.minHeight || 20;
49777 };
49778
49779 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49780     hide : function(){
49781         // center panel can't be hidden
49782     },
49783     
49784     show : function(){
49785         // center panel can't be hidden
49786     },
49787     
49788     getMinWidth: function(){
49789         return this.minWidth;
49790     },
49791     
49792     getMinHeight: function(){
49793         return this.minHeight;
49794     }
49795 });
49796
49797
49798 Roo.NorthLayoutRegion = function(mgr, config){
49799     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49800     if(this.split){
49801         this.split.placement = Roo.SplitBar.TOP;
49802         this.split.orientation = Roo.SplitBar.VERTICAL;
49803         this.split.el.addClass("x-layout-split-v");
49804     }
49805     var size = config.initialSize || config.height;
49806     if(typeof size != "undefined"){
49807         this.el.setHeight(size);
49808     }
49809 };
49810 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49811     orientation: Roo.SplitBar.VERTICAL,
49812     getBox : function(){
49813         if(this.collapsed){
49814             return this.collapsedEl.getBox();
49815         }
49816         var box = this.el.getBox();
49817         if(this.split){
49818             box.height += this.split.el.getHeight();
49819         }
49820         return box;
49821     },
49822     
49823     updateBox : function(box){
49824         if(this.split && !this.collapsed){
49825             box.height -= this.split.el.getHeight();
49826             this.split.el.setLeft(box.x);
49827             this.split.el.setTop(box.y+box.height);
49828             this.split.el.setWidth(box.width);
49829         }
49830         if(this.collapsed){
49831             this.updateBody(box.width, null);
49832         }
49833         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49834     }
49835 });
49836
49837 Roo.SouthLayoutRegion = function(mgr, config){
49838     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49839     if(this.split){
49840         this.split.placement = Roo.SplitBar.BOTTOM;
49841         this.split.orientation = Roo.SplitBar.VERTICAL;
49842         this.split.el.addClass("x-layout-split-v");
49843     }
49844     var size = config.initialSize || config.height;
49845     if(typeof size != "undefined"){
49846         this.el.setHeight(size);
49847     }
49848 };
49849 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49850     orientation: Roo.SplitBar.VERTICAL,
49851     getBox : function(){
49852         if(this.collapsed){
49853             return this.collapsedEl.getBox();
49854         }
49855         var box = this.el.getBox();
49856         if(this.split){
49857             var sh = this.split.el.getHeight();
49858             box.height += sh;
49859             box.y -= sh;
49860         }
49861         return box;
49862     },
49863     
49864     updateBox : function(box){
49865         if(this.split && !this.collapsed){
49866             var sh = this.split.el.getHeight();
49867             box.height -= sh;
49868             box.y += sh;
49869             this.split.el.setLeft(box.x);
49870             this.split.el.setTop(box.y-sh);
49871             this.split.el.setWidth(box.width);
49872         }
49873         if(this.collapsed){
49874             this.updateBody(box.width, null);
49875         }
49876         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49877     }
49878 });
49879
49880 Roo.EastLayoutRegion = function(mgr, config){
49881     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49882     if(this.split){
49883         this.split.placement = Roo.SplitBar.RIGHT;
49884         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49885         this.split.el.addClass("x-layout-split-h");
49886     }
49887     var size = config.initialSize || config.width;
49888     if(typeof size != "undefined"){
49889         this.el.setWidth(size);
49890     }
49891 };
49892 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49893     orientation: Roo.SplitBar.HORIZONTAL,
49894     getBox : function(){
49895         if(this.collapsed){
49896             return this.collapsedEl.getBox();
49897         }
49898         var box = this.el.getBox();
49899         if(this.split){
49900             var sw = this.split.el.getWidth();
49901             box.width += sw;
49902             box.x -= sw;
49903         }
49904         return box;
49905     },
49906
49907     updateBox : function(box){
49908         if(this.split && !this.collapsed){
49909             var sw = this.split.el.getWidth();
49910             box.width -= sw;
49911             this.split.el.setLeft(box.x);
49912             this.split.el.setTop(box.y);
49913             this.split.el.setHeight(box.height);
49914             box.x += sw;
49915         }
49916         if(this.collapsed){
49917             this.updateBody(null, box.height);
49918         }
49919         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49920     }
49921 });
49922
49923 Roo.WestLayoutRegion = function(mgr, config){
49924     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49925     if(this.split){
49926         this.split.placement = Roo.SplitBar.LEFT;
49927         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49928         this.split.el.addClass("x-layout-split-h");
49929     }
49930     var size = config.initialSize || config.width;
49931     if(typeof size != "undefined"){
49932         this.el.setWidth(size);
49933     }
49934 };
49935 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49936     orientation: Roo.SplitBar.HORIZONTAL,
49937     getBox : function(){
49938         if(this.collapsed){
49939             return this.collapsedEl.getBox();
49940         }
49941         var box = this.el.getBox();
49942         if(this.split){
49943             box.width += this.split.el.getWidth();
49944         }
49945         return box;
49946     },
49947     
49948     updateBox : function(box){
49949         if(this.split && !this.collapsed){
49950             var sw = this.split.el.getWidth();
49951             box.width -= sw;
49952             this.split.el.setLeft(box.x+box.width);
49953             this.split.el.setTop(box.y);
49954             this.split.el.setHeight(box.height);
49955         }
49956         if(this.collapsed){
49957             this.updateBody(null, box.height);
49958         }
49959         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49960     }
49961 });
49962 /*
49963  * Based on:
49964  * Ext JS Library 1.1.1
49965  * Copyright(c) 2006-2007, Ext JS, LLC.
49966  *
49967  * Originally Released Under LGPL - original licence link has changed is not relivant.
49968  *
49969  * Fork - LGPL
49970  * <script type="text/javascript">
49971  */
49972  
49973  
49974 /*
49975  * Private internal class for reading and applying state
49976  */
49977 Roo.LayoutStateManager = function(layout){
49978      // default empty state
49979      this.state = {
49980         north: {},
49981         south: {},
49982         east: {},
49983         west: {}       
49984     };
49985 };
49986
49987 Roo.LayoutStateManager.prototype = {
49988     init : function(layout, provider){
49989         this.provider = provider;
49990         var state = provider.get(layout.id+"-layout-state");
49991         if(state){
49992             var wasUpdating = layout.isUpdating();
49993             if(!wasUpdating){
49994                 layout.beginUpdate();
49995             }
49996             for(var key in state){
49997                 if(typeof state[key] != "function"){
49998                     var rstate = state[key];
49999                     var r = layout.getRegion(key);
50000                     if(r && rstate){
50001                         if(rstate.size){
50002                             r.resizeTo(rstate.size);
50003                         }
50004                         if(rstate.collapsed == true){
50005                             r.collapse(true);
50006                         }else{
50007                             r.expand(null, true);
50008                         }
50009                     }
50010                 }
50011             }
50012             if(!wasUpdating){
50013                 layout.endUpdate();
50014             }
50015             this.state = state; 
50016         }
50017         this.layout = layout;
50018         layout.on("regionresized", this.onRegionResized, this);
50019         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50020         layout.on("regionexpanded", this.onRegionExpanded, this);
50021     },
50022     
50023     storeState : function(){
50024         this.provider.set(this.layout.id+"-layout-state", this.state);
50025     },
50026     
50027     onRegionResized : function(region, newSize){
50028         this.state[region.getPosition()].size = newSize;
50029         this.storeState();
50030     },
50031     
50032     onRegionCollapsed : function(region){
50033         this.state[region.getPosition()].collapsed = true;
50034         this.storeState();
50035     },
50036     
50037     onRegionExpanded : function(region){
50038         this.state[region.getPosition()].collapsed = false;
50039         this.storeState();
50040     }
50041 };/*
50042  * Based on:
50043  * Ext JS Library 1.1.1
50044  * Copyright(c) 2006-2007, Ext JS, LLC.
50045  *
50046  * Originally Released Under LGPL - original licence link has changed is not relivant.
50047  *
50048  * Fork - LGPL
50049  * <script type="text/javascript">
50050  */
50051 /**
50052  * @class Roo.ContentPanel
50053  * @extends Roo.util.Observable
50054  * A basic ContentPanel element.
50055  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50056  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50057  * @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
50058  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50059  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50060  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50061  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50062  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50063  * @cfg {String} title          The title for this panel
50064  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50065  * @cfg {String} url            Calls {@link #setUrl} with this value
50066  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50067  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50068  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50069  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50070
50071  * @constructor
50072  * Create a new ContentPanel.
50073  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50074  * @param {String/Object} config A string to set only the title or a config object
50075  * @param {String} content (optional) Set the HTML content for this panel
50076  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50077  */
50078 Roo.ContentPanel = function(el, config, content){
50079     
50080      
50081     /*
50082     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50083         config = el;
50084         el = Roo.id();
50085     }
50086     if (config && config.parentLayout) { 
50087         el = config.parentLayout.el.createChild(); 
50088     }
50089     */
50090     if(el.autoCreate){ // xtype is available if this is called from factory
50091         config = el;
50092         el = Roo.id();
50093     }
50094     this.el = Roo.get(el);
50095     if(!this.el && config && config.autoCreate){
50096         if(typeof config.autoCreate == "object"){
50097             if(!config.autoCreate.id){
50098                 config.autoCreate.id = config.id||el;
50099             }
50100             this.el = Roo.DomHelper.append(document.body,
50101                         config.autoCreate, true);
50102         }else{
50103             this.el = Roo.DomHelper.append(document.body,
50104                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50105         }
50106     }
50107     this.closable = false;
50108     this.loaded = false;
50109     this.active = false;
50110     if(typeof config == "string"){
50111         this.title = config;
50112     }else{
50113         Roo.apply(this, config);
50114     }
50115     
50116     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50117         this.wrapEl = this.el.wrap();
50118         this.toolbar.container = this.el.insertSibling(false, 'before');
50119         this.toolbar = new Roo.Toolbar(this.toolbar);
50120     }
50121     
50122     // xtype created footer. - not sure if will work as we normally have to render first..
50123     if (this.footer && !this.footer.el && this.footer.xtype) {
50124         if (!this.wrapEl) {
50125             this.wrapEl = this.el.wrap();
50126         }
50127     
50128         this.footer.container = this.wrapEl.createChild();
50129          
50130         this.footer = Roo.factory(this.footer, Roo);
50131         
50132     }
50133     
50134     if(this.resizeEl){
50135         this.resizeEl = Roo.get(this.resizeEl, true);
50136     }else{
50137         this.resizeEl = this.el;
50138     }
50139     // handle view.xtype
50140     
50141  
50142     
50143     
50144     this.addEvents({
50145         /**
50146          * @event activate
50147          * Fires when this panel is activated. 
50148          * @param {Roo.ContentPanel} this
50149          */
50150         "activate" : true,
50151         /**
50152          * @event deactivate
50153          * Fires when this panel is activated. 
50154          * @param {Roo.ContentPanel} this
50155          */
50156         "deactivate" : true,
50157
50158         /**
50159          * @event resize
50160          * Fires when this panel is resized if fitToFrame is true.
50161          * @param {Roo.ContentPanel} this
50162          * @param {Number} width The width after any component adjustments
50163          * @param {Number} height The height after any component adjustments
50164          */
50165         "resize" : true,
50166         
50167          /**
50168          * @event render
50169          * Fires when this tab is created
50170          * @param {Roo.ContentPanel} this
50171          */
50172         "render" : true
50173         
50174         
50175         
50176     });
50177     
50178
50179     
50180     
50181     if(this.autoScroll){
50182         this.resizeEl.setStyle("overflow", "auto");
50183     } else {
50184         // fix randome scrolling
50185         this.el.on('scroll', function() {
50186             Roo.log('fix random scolling');
50187             this.scrollTo('top',0); 
50188         });
50189     }
50190     content = content || this.content;
50191     if(content){
50192         this.setContent(content);
50193     }
50194     if(config && config.url){
50195         this.setUrl(this.url, this.params, this.loadOnce);
50196     }
50197     
50198     
50199     
50200     Roo.ContentPanel.superclass.constructor.call(this);
50201     
50202     if (this.view && typeof(this.view.xtype) != 'undefined') {
50203         this.view.el = this.el.appendChild(document.createElement("div"));
50204         this.view = Roo.factory(this.view); 
50205         this.view.render  &&  this.view.render(false, '');  
50206     }
50207     
50208     
50209     this.fireEvent('render', this);
50210 };
50211
50212 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50213     tabTip:'',
50214     setRegion : function(region){
50215         this.region = region;
50216         if(region){
50217            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50218         }else{
50219            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50220         } 
50221     },
50222     
50223     /**
50224      * Returns the toolbar for this Panel if one was configured. 
50225      * @return {Roo.Toolbar} 
50226      */
50227     getToolbar : function(){
50228         return this.toolbar;
50229     },
50230     
50231     setActiveState : function(active){
50232         this.active = active;
50233         if(!active){
50234             this.fireEvent("deactivate", this);
50235         }else{
50236             this.fireEvent("activate", this);
50237         }
50238     },
50239     /**
50240      * Updates this panel's element
50241      * @param {String} content The new content
50242      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50243     */
50244     setContent : function(content, loadScripts){
50245         this.el.update(content, loadScripts);
50246     },
50247
50248     ignoreResize : function(w, h){
50249         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50250             return true;
50251         }else{
50252             this.lastSize = {width: w, height: h};
50253             return false;
50254         }
50255     },
50256     /**
50257      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50258      * @return {Roo.UpdateManager} The UpdateManager
50259      */
50260     getUpdateManager : function(){
50261         return this.el.getUpdateManager();
50262     },
50263      /**
50264      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50265      * @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:
50266 <pre><code>
50267 panel.load({
50268     url: "your-url.php",
50269     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50270     callback: yourFunction,
50271     scope: yourObject, //(optional scope)
50272     discardUrl: false,
50273     nocache: false,
50274     text: "Loading...",
50275     timeout: 30,
50276     scripts: false
50277 });
50278 </code></pre>
50279      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50280      * 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.
50281      * @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}
50282      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50283      * @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.
50284      * @return {Roo.ContentPanel} this
50285      */
50286     load : function(){
50287         var um = this.el.getUpdateManager();
50288         um.update.apply(um, arguments);
50289         return this;
50290     },
50291
50292
50293     /**
50294      * 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.
50295      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50296      * @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)
50297      * @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)
50298      * @return {Roo.UpdateManager} The UpdateManager
50299      */
50300     setUrl : function(url, params, loadOnce){
50301         if(this.refreshDelegate){
50302             this.removeListener("activate", this.refreshDelegate);
50303         }
50304         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50305         this.on("activate", this.refreshDelegate);
50306         return this.el.getUpdateManager();
50307     },
50308     
50309     _handleRefresh : function(url, params, loadOnce){
50310         if(!loadOnce || !this.loaded){
50311             var updater = this.el.getUpdateManager();
50312             updater.update(url, params, this._setLoaded.createDelegate(this));
50313         }
50314     },
50315     
50316     _setLoaded : function(){
50317         this.loaded = true;
50318     }, 
50319     
50320     /**
50321      * Returns this panel's id
50322      * @return {String} 
50323      */
50324     getId : function(){
50325         return this.el.id;
50326     },
50327     
50328     /** 
50329      * Returns this panel's element - used by regiosn to add.
50330      * @return {Roo.Element} 
50331      */
50332     getEl : function(){
50333         return this.wrapEl || this.el;
50334     },
50335     
50336     adjustForComponents : function(width, height)
50337     {
50338         //Roo.log('adjustForComponents ');
50339         if(this.resizeEl != this.el){
50340             width -= this.el.getFrameWidth('lr');
50341             height -= this.el.getFrameWidth('tb');
50342         }
50343         if(this.toolbar){
50344             var te = this.toolbar.getEl();
50345             height -= te.getHeight();
50346             te.setWidth(width);
50347         }
50348         if(this.footer){
50349             var te = this.footer.getEl();
50350             Roo.log("footer:" + te.getHeight());
50351             
50352             height -= te.getHeight();
50353             te.setWidth(width);
50354         }
50355         
50356         
50357         if(this.adjustments){
50358             width += this.adjustments[0];
50359             height += this.adjustments[1];
50360         }
50361         return {"width": width, "height": height};
50362     },
50363     
50364     setSize : function(width, height){
50365         if(this.fitToFrame && !this.ignoreResize(width, height)){
50366             if(this.fitContainer && this.resizeEl != this.el){
50367                 this.el.setSize(width, height);
50368             }
50369             var size = this.adjustForComponents(width, height);
50370             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50371             this.fireEvent('resize', this, size.width, size.height);
50372         }
50373     },
50374     
50375     /**
50376      * Returns this panel's title
50377      * @return {String} 
50378      */
50379     getTitle : function(){
50380         return this.title;
50381     },
50382     
50383     /**
50384      * Set this panel's title
50385      * @param {String} title
50386      */
50387     setTitle : function(title){
50388         this.title = title;
50389         if(this.region){
50390             this.region.updatePanelTitle(this, title);
50391         }
50392     },
50393     
50394     /**
50395      * Returns true is this panel was configured to be closable
50396      * @return {Boolean} 
50397      */
50398     isClosable : function(){
50399         return this.closable;
50400     },
50401     
50402     beforeSlide : function(){
50403         this.el.clip();
50404         this.resizeEl.clip();
50405     },
50406     
50407     afterSlide : function(){
50408         this.el.unclip();
50409         this.resizeEl.unclip();
50410     },
50411     
50412     /**
50413      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50414      *   Will fail silently if the {@link #setUrl} method has not been called.
50415      *   This does not activate the panel, just updates its content.
50416      */
50417     refresh : function(){
50418         if(this.refreshDelegate){
50419            this.loaded = false;
50420            this.refreshDelegate();
50421         }
50422     },
50423     
50424     /**
50425      * Destroys this panel
50426      */
50427     destroy : function(){
50428         this.el.removeAllListeners();
50429         var tempEl = document.createElement("span");
50430         tempEl.appendChild(this.el.dom);
50431         tempEl.innerHTML = "";
50432         this.el.remove();
50433         this.el = null;
50434     },
50435     
50436     /**
50437      * form - if the content panel contains a form - this is a reference to it.
50438      * @type {Roo.form.Form}
50439      */
50440     form : false,
50441     /**
50442      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50443      *    This contains a reference to it.
50444      * @type {Roo.View}
50445      */
50446     view : false,
50447     
50448       /**
50449      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50450      * <pre><code>
50451
50452 layout.addxtype({
50453        xtype : 'Form',
50454        items: [ .... ]
50455    }
50456 );
50457
50458 </code></pre>
50459      * @param {Object} cfg Xtype definition of item to add.
50460      */
50461     
50462     addxtype : function(cfg) {
50463         // add form..
50464         if (cfg.xtype.match(/^Form$/)) {
50465             
50466             var el;
50467             //if (this.footer) {
50468             //    el = this.footer.container.insertSibling(false, 'before');
50469             //} else {
50470                 el = this.el.createChild();
50471             //}
50472
50473             this.form = new  Roo.form.Form(cfg);
50474             
50475             
50476             if ( this.form.allItems.length) this.form.render(el.dom);
50477             return this.form;
50478         }
50479         // should only have one of theses..
50480         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50481             // views.. should not be just added - used named prop 'view''
50482             
50483             cfg.el = this.el.appendChild(document.createElement("div"));
50484             // factory?
50485             
50486             var ret = new Roo.factory(cfg);
50487              
50488              ret.render && ret.render(false, ''); // render blank..
50489             this.view = ret;
50490             return ret;
50491         }
50492         return false;
50493     }
50494 });
50495
50496 /**
50497  * @class Roo.GridPanel
50498  * @extends Roo.ContentPanel
50499  * @constructor
50500  * Create a new GridPanel.
50501  * @param {Roo.grid.Grid} grid The grid for this panel
50502  * @param {String/Object} config A string to set only the panel's title, or a config object
50503  */
50504 Roo.GridPanel = function(grid, config){
50505     
50506   
50507     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50508         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50509         
50510     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50511     
50512     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50513     
50514     if(this.toolbar){
50515         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50516     }
50517     // xtype created footer. - not sure if will work as we normally have to render first..
50518     if (this.footer && !this.footer.el && this.footer.xtype) {
50519         
50520         this.footer.container = this.grid.getView().getFooterPanel(true);
50521         this.footer.dataSource = this.grid.dataSource;
50522         this.footer = Roo.factory(this.footer, Roo);
50523         
50524     }
50525     
50526     grid.monitorWindowResize = false; // turn off autosizing
50527     grid.autoHeight = false;
50528     grid.autoWidth = false;
50529     this.grid = grid;
50530     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50531 };
50532
50533 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50534     getId : function(){
50535         return this.grid.id;
50536     },
50537     
50538     /**
50539      * Returns the grid for this panel
50540      * @return {Roo.grid.Grid} 
50541      */
50542     getGrid : function(){
50543         return this.grid;    
50544     },
50545     
50546     setSize : function(width, height){
50547         if(!this.ignoreResize(width, height)){
50548             var grid = this.grid;
50549             var size = this.adjustForComponents(width, height);
50550             grid.getGridEl().setSize(size.width, size.height);
50551             grid.autoSize();
50552         }
50553     },
50554     
50555     beforeSlide : function(){
50556         this.grid.getView().scroller.clip();
50557     },
50558     
50559     afterSlide : function(){
50560         this.grid.getView().scroller.unclip();
50561     },
50562     
50563     destroy : function(){
50564         this.grid.destroy();
50565         delete this.grid;
50566         Roo.GridPanel.superclass.destroy.call(this); 
50567     }
50568 });
50569
50570
50571 /**
50572  * @class Roo.NestedLayoutPanel
50573  * @extends Roo.ContentPanel
50574  * @constructor
50575  * Create a new NestedLayoutPanel.
50576  * 
50577  * 
50578  * @param {Roo.BorderLayout} layout The layout for this panel
50579  * @param {String/Object} config A string to set only the title or a config object
50580  */
50581 Roo.NestedLayoutPanel = function(layout, config)
50582 {
50583     // construct with only one argument..
50584     /* FIXME - implement nicer consturctors
50585     if (layout.layout) {
50586         config = layout;
50587         layout = config.layout;
50588         delete config.layout;
50589     }
50590     if (layout.xtype && !layout.getEl) {
50591         // then layout needs constructing..
50592         layout = Roo.factory(layout, Roo);
50593     }
50594     */
50595     
50596     
50597     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50598     
50599     layout.monitorWindowResize = false; // turn off autosizing
50600     this.layout = layout;
50601     this.layout.getEl().addClass("x-layout-nested-layout");
50602     
50603     
50604     
50605     
50606 };
50607
50608 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50609
50610     setSize : function(width, height){
50611         if(!this.ignoreResize(width, height)){
50612             var size = this.adjustForComponents(width, height);
50613             var el = this.layout.getEl();
50614             el.setSize(size.width, size.height);
50615             var touch = el.dom.offsetWidth;
50616             this.layout.layout();
50617             // ie requires a double layout on the first pass
50618             if(Roo.isIE && !this.initialized){
50619                 this.initialized = true;
50620                 this.layout.layout();
50621             }
50622         }
50623     },
50624     
50625     // activate all subpanels if not currently active..
50626     
50627     setActiveState : function(active){
50628         this.active = active;
50629         if(!active){
50630             this.fireEvent("deactivate", this);
50631             return;
50632         }
50633         
50634         this.fireEvent("activate", this);
50635         // not sure if this should happen before or after..
50636         if (!this.layout) {
50637             return; // should not happen..
50638         }
50639         var reg = false;
50640         for (var r in this.layout.regions) {
50641             reg = this.layout.getRegion(r);
50642             if (reg.getActivePanel()) {
50643                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50644                 reg.setActivePanel(reg.getActivePanel());
50645                 continue;
50646             }
50647             if (!reg.panels.length) {
50648                 continue;
50649             }
50650             reg.showPanel(reg.getPanel(0));
50651         }
50652         
50653         
50654         
50655         
50656     },
50657     
50658     /**
50659      * Returns the nested BorderLayout for this panel
50660      * @return {Roo.BorderLayout} 
50661      */
50662     getLayout : function(){
50663         return this.layout;
50664     },
50665     
50666      /**
50667      * Adds a xtype elements to the layout of the nested panel
50668      * <pre><code>
50669
50670 panel.addxtype({
50671        xtype : 'ContentPanel',
50672        region: 'west',
50673        items: [ .... ]
50674    }
50675 );
50676
50677 panel.addxtype({
50678         xtype : 'NestedLayoutPanel',
50679         region: 'west',
50680         layout: {
50681            center: { },
50682            west: { }   
50683         },
50684         items : [ ... list of content panels or nested layout panels.. ]
50685    }
50686 );
50687 </code></pre>
50688      * @param {Object} cfg Xtype definition of item to add.
50689      */
50690     addxtype : function(cfg) {
50691         return this.layout.addxtype(cfg);
50692     
50693     }
50694 });
50695
50696 Roo.ScrollPanel = function(el, config, content){
50697     config = config || {};
50698     config.fitToFrame = true;
50699     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50700     
50701     this.el.dom.style.overflow = "hidden";
50702     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50703     this.el.removeClass("x-layout-inactive-content");
50704     this.el.on("mousewheel", this.onWheel, this);
50705
50706     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50707     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50708     up.unselectable(); down.unselectable();
50709     up.on("click", this.scrollUp, this);
50710     down.on("click", this.scrollDown, this);
50711     up.addClassOnOver("x-scroller-btn-over");
50712     down.addClassOnOver("x-scroller-btn-over");
50713     up.addClassOnClick("x-scroller-btn-click");
50714     down.addClassOnClick("x-scroller-btn-click");
50715     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50716
50717     this.resizeEl = this.el;
50718     this.el = wrap; this.up = up; this.down = down;
50719 };
50720
50721 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50722     increment : 100,
50723     wheelIncrement : 5,
50724     scrollUp : function(){
50725         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50726     },
50727
50728     scrollDown : function(){
50729         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50730     },
50731
50732     afterScroll : function(){
50733         var el = this.resizeEl;
50734         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50735         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50736         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50737     },
50738
50739     setSize : function(){
50740         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50741         this.afterScroll();
50742     },
50743
50744     onWheel : function(e){
50745         var d = e.getWheelDelta();
50746         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50747         this.afterScroll();
50748         e.stopEvent();
50749     },
50750
50751     setContent : function(content, loadScripts){
50752         this.resizeEl.update(content, loadScripts);
50753     }
50754
50755 });
50756
50757
50758
50759
50760
50761
50762
50763
50764
50765 /**
50766  * @class Roo.TreePanel
50767  * @extends Roo.ContentPanel
50768  * @constructor
50769  * Create a new TreePanel. - defaults to fit/scoll contents.
50770  * @param {String/Object} config A string to set only the panel's title, or a config object
50771  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50772  */
50773 Roo.TreePanel = function(config){
50774     var el = config.el;
50775     var tree = config.tree;
50776     delete config.tree; 
50777     delete config.el; // hopefull!
50778     
50779     // wrapper for IE7 strict & safari scroll issue
50780     
50781     var treeEl = el.createChild();
50782     config.resizeEl = treeEl;
50783     
50784     
50785     
50786     Roo.TreePanel.superclass.constructor.call(this, el, config);
50787  
50788  
50789     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50790     //console.log(tree);
50791     this.on('activate', function()
50792     {
50793         if (this.tree.rendered) {
50794             return;
50795         }
50796         //console.log('render tree');
50797         this.tree.render();
50798     });
50799     // this should not be needed.. - it's actually the 'el' that resizes?
50800     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50801     
50802     //this.on('resize',  function (cp, w, h) {
50803     //        this.tree.innerCt.setWidth(w);
50804     //        this.tree.innerCt.setHeight(h);
50805     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50806     //});
50807
50808         
50809     
50810 };
50811
50812 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50813     fitToFrame : true,
50814     autoScroll : true
50815 });
50816
50817
50818
50819
50820
50821
50822
50823
50824
50825
50826
50827 /*
50828  * Based on:
50829  * Ext JS Library 1.1.1
50830  * Copyright(c) 2006-2007, Ext JS, LLC.
50831  *
50832  * Originally Released Under LGPL - original licence link has changed is not relivant.
50833  *
50834  * Fork - LGPL
50835  * <script type="text/javascript">
50836  */
50837  
50838
50839 /**
50840  * @class Roo.ReaderLayout
50841  * @extends Roo.BorderLayout
50842  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50843  * center region containing two nested regions (a top one for a list view and one for item preview below),
50844  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50845  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50846  * expedites the setup of the overall layout and regions for this common application style.
50847  * Example:
50848  <pre><code>
50849 var reader = new Roo.ReaderLayout();
50850 var CP = Roo.ContentPanel;  // shortcut for adding
50851
50852 reader.beginUpdate();
50853 reader.add("north", new CP("north", "North"));
50854 reader.add("west", new CP("west", {title: "West"}));
50855 reader.add("east", new CP("east", {title: "East"}));
50856
50857 reader.regions.listView.add(new CP("listView", "List"));
50858 reader.regions.preview.add(new CP("preview", "Preview"));
50859 reader.endUpdate();
50860 </code></pre>
50861 * @constructor
50862 * Create a new ReaderLayout
50863 * @param {Object} config Configuration options
50864 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50865 * document.body if omitted)
50866 */
50867 Roo.ReaderLayout = function(config, renderTo){
50868     var c = config || {size:{}};
50869     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50870         north: c.north !== false ? Roo.apply({
50871             split:false,
50872             initialSize: 32,
50873             titlebar: false
50874         }, c.north) : false,
50875         west: c.west !== false ? Roo.apply({
50876             split:true,
50877             initialSize: 200,
50878             minSize: 175,
50879             maxSize: 400,
50880             titlebar: true,
50881             collapsible: true,
50882             animate: true,
50883             margins:{left:5,right:0,bottom:5,top:5},
50884             cmargins:{left:5,right:5,bottom:5,top:5}
50885         }, c.west) : false,
50886         east: c.east !== false ? Roo.apply({
50887             split:true,
50888             initialSize: 200,
50889             minSize: 175,
50890             maxSize: 400,
50891             titlebar: true,
50892             collapsible: true,
50893             animate: true,
50894             margins:{left:0,right:5,bottom:5,top:5},
50895             cmargins:{left:5,right:5,bottom:5,top:5}
50896         }, c.east) : false,
50897         center: Roo.apply({
50898             tabPosition: 'top',
50899             autoScroll:false,
50900             closeOnTab: true,
50901             titlebar:false,
50902             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50903         }, c.center)
50904     });
50905
50906     this.el.addClass('x-reader');
50907
50908     this.beginUpdate();
50909
50910     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50911         south: c.preview !== false ? Roo.apply({
50912             split:true,
50913             initialSize: 200,
50914             minSize: 100,
50915             autoScroll:true,
50916             collapsible:true,
50917             titlebar: true,
50918             cmargins:{top:5,left:0, right:0, bottom:0}
50919         }, c.preview) : false,
50920         center: Roo.apply({
50921             autoScroll:false,
50922             titlebar:false,
50923             minHeight:200
50924         }, c.listView)
50925     });
50926     this.add('center', new Roo.NestedLayoutPanel(inner,
50927             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50928
50929     this.endUpdate();
50930
50931     this.regions.preview = inner.getRegion('south');
50932     this.regions.listView = inner.getRegion('center');
50933 };
50934
50935 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50936  * Based on:
50937  * Ext JS Library 1.1.1
50938  * Copyright(c) 2006-2007, Ext JS, LLC.
50939  *
50940  * Originally Released Under LGPL - original licence link has changed is not relivant.
50941  *
50942  * Fork - LGPL
50943  * <script type="text/javascript">
50944  */
50945  
50946 /**
50947  * @class Roo.grid.Grid
50948  * @extends Roo.util.Observable
50949  * This class represents the primary interface of a component based grid control.
50950  * <br><br>Usage:<pre><code>
50951  var grid = new Roo.grid.Grid("my-container-id", {
50952      ds: myDataStore,
50953      cm: myColModel,
50954      selModel: mySelectionModel,
50955      autoSizeColumns: true,
50956      monitorWindowResize: false,
50957      trackMouseOver: true
50958  });
50959  // set any options
50960  grid.render();
50961  * </code></pre>
50962  * <b>Common Problems:</b><br/>
50963  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
50964  * element will correct this<br/>
50965  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
50966  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
50967  * are unpredictable.<br/>
50968  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
50969  * grid to calculate dimensions/offsets.<br/>
50970   * @constructor
50971  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50972  * The container MUST have some type of size defined for the grid to fill. The container will be
50973  * automatically set to position relative if it isn't already.
50974  * @param {Object} config A config object that sets properties on this grid.
50975  */
50976 Roo.grid.Grid = function(container, config){
50977         // initialize the container
50978         this.container = Roo.get(container);
50979         this.container.update("");
50980         this.container.setStyle("overflow", "hidden");
50981     this.container.addClass('x-grid-container');
50982
50983     this.id = this.container.id;
50984
50985     Roo.apply(this, config);
50986     // check and correct shorthanded configs
50987     if(this.ds){
50988         this.dataSource = this.ds;
50989         delete this.ds;
50990     }
50991     if(this.cm){
50992         this.colModel = this.cm;
50993         delete this.cm;
50994     }
50995     if(this.sm){
50996         this.selModel = this.sm;
50997         delete this.sm;
50998     }
50999
51000     if (this.selModel) {
51001         this.selModel = Roo.factory(this.selModel, Roo.grid);
51002         this.sm = this.selModel;
51003         this.sm.xmodule = this.xmodule || false;
51004     }
51005     if (typeof(this.colModel.config) == 'undefined') {
51006         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51007         this.cm = this.colModel;
51008         this.cm.xmodule = this.xmodule || false;
51009     }
51010     if (this.dataSource) {
51011         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51012         this.ds = this.dataSource;
51013         this.ds.xmodule = this.xmodule || false;
51014          
51015     }
51016     
51017     
51018     
51019     if(this.width){
51020         this.container.setWidth(this.width);
51021     }
51022
51023     if(this.height){
51024         this.container.setHeight(this.height);
51025     }
51026     /** @private */
51027         this.addEvents({
51028         // raw events
51029         /**
51030          * @event click
51031          * The raw click event for the entire grid.
51032          * @param {Roo.EventObject} e
51033          */
51034         "click" : true,
51035         /**
51036          * @event dblclick
51037          * The raw dblclick event for the entire grid.
51038          * @param {Roo.EventObject} e
51039          */
51040         "dblclick" : true,
51041         /**
51042          * @event contextmenu
51043          * The raw contextmenu event for the entire grid.
51044          * @param {Roo.EventObject} e
51045          */
51046         "contextmenu" : true,
51047         /**
51048          * @event mousedown
51049          * The raw mousedown event for the entire grid.
51050          * @param {Roo.EventObject} e
51051          */
51052         "mousedown" : true,
51053         /**
51054          * @event mouseup
51055          * The raw mouseup event for the entire grid.
51056          * @param {Roo.EventObject} e
51057          */
51058         "mouseup" : true,
51059         /**
51060          * @event mouseover
51061          * The raw mouseover event for the entire grid.
51062          * @param {Roo.EventObject} e
51063          */
51064         "mouseover" : true,
51065         /**
51066          * @event mouseout
51067          * The raw mouseout event for the entire grid.
51068          * @param {Roo.EventObject} e
51069          */
51070         "mouseout" : true,
51071         /**
51072          * @event keypress
51073          * The raw keypress event for the entire grid.
51074          * @param {Roo.EventObject} e
51075          */
51076         "keypress" : true,
51077         /**
51078          * @event keydown
51079          * The raw keydown event for the entire grid.
51080          * @param {Roo.EventObject} e
51081          */
51082         "keydown" : true,
51083
51084         // custom events
51085
51086         /**
51087          * @event cellclick
51088          * Fires when a cell is clicked
51089          * @param {Grid} this
51090          * @param {Number} rowIndex
51091          * @param {Number} columnIndex
51092          * @param {Roo.EventObject} e
51093          */
51094         "cellclick" : true,
51095         /**
51096          * @event celldblclick
51097          * Fires when a cell is double clicked
51098          * @param {Grid} this
51099          * @param {Number} rowIndex
51100          * @param {Number} columnIndex
51101          * @param {Roo.EventObject} e
51102          */
51103         "celldblclick" : true,
51104         /**
51105          * @event rowclick
51106          * Fires when a row is clicked
51107          * @param {Grid} this
51108          * @param {Number} rowIndex
51109          * @param {Roo.EventObject} e
51110          */
51111         "rowclick" : true,
51112         /**
51113          * @event rowdblclick
51114          * Fires when a row is double clicked
51115          * @param {Grid} this
51116          * @param {Number} rowIndex
51117          * @param {Roo.EventObject} e
51118          */
51119         "rowdblclick" : true,
51120         /**
51121          * @event headerclick
51122          * Fires when a header is clicked
51123          * @param {Grid} this
51124          * @param {Number} columnIndex
51125          * @param {Roo.EventObject} e
51126          */
51127         "headerclick" : true,
51128         /**
51129          * @event headerdblclick
51130          * Fires when a header cell is double clicked
51131          * @param {Grid} this
51132          * @param {Number} columnIndex
51133          * @param {Roo.EventObject} e
51134          */
51135         "headerdblclick" : true,
51136         /**
51137          * @event rowcontextmenu
51138          * Fires when a row is right clicked
51139          * @param {Grid} this
51140          * @param {Number} rowIndex
51141          * @param {Roo.EventObject} e
51142          */
51143         "rowcontextmenu" : true,
51144         /**
51145          * @event cellcontextmenu
51146          * Fires when a cell is right clicked
51147          * @param {Grid} this
51148          * @param {Number} rowIndex
51149          * @param {Number} cellIndex
51150          * @param {Roo.EventObject} e
51151          */
51152          "cellcontextmenu" : true,
51153         /**
51154          * @event headercontextmenu
51155          * Fires when a header is right clicked
51156          * @param {Grid} this
51157          * @param {Number} columnIndex
51158          * @param {Roo.EventObject} e
51159          */
51160         "headercontextmenu" : true,
51161         /**
51162          * @event bodyscroll
51163          * Fires when the body element is scrolled
51164          * @param {Number} scrollLeft
51165          * @param {Number} scrollTop
51166          */
51167         "bodyscroll" : true,
51168         /**
51169          * @event columnresize
51170          * Fires when the user resizes a column
51171          * @param {Number} columnIndex
51172          * @param {Number} newSize
51173          */
51174         "columnresize" : true,
51175         /**
51176          * @event columnmove
51177          * Fires when the user moves a column
51178          * @param {Number} oldIndex
51179          * @param {Number} newIndex
51180          */
51181         "columnmove" : true,
51182         /**
51183          * @event startdrag
51184          * Fires when row(s) start being dragged
51185          * @param {Grid} this
51186          * @param {Roo.GridDD} dd The drag drop object
51187          * @param {event} e The raw browser event
51188          */
51189         "startdrag" : true,
51190         /**
51191          * @event enddrag
51192          * Fires when a drag operation is complete
51193          * @param {Grid} this
51194          * @param {Roo.GridDD} dd The drag drop object
51195          * @param {event} e The raw browser event
51196          */
51197         "enddrag" : true,
51198         /**
51199          * @event dragdrop
51200          * Fires when dragged row(s) are dropped on a valid DD target
51201          * @param {Grid} this
51202          * @param {Roo.GridDD} dd The drag drop object
51203          * @param {String} targetId The target drag drop object
51204          * @param {event} e The raw browser event
51205          */
51206         "dragdrop" : true,
51207         /**
51208          * @event dragover
51209          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51210          * @param {Grid} this
51211          * @param {Roo.GridDD} dd The drag drop object
51212          * @param {String} targetId The target drag drop object
51213          * @param {event} e The raw browser event
51214          */
51215         "dragover" : true,
51216         /**
51217          * @event dragenter
51218          *  Fires when the dragged row(s) first cross another DD target while being dragged
51219          * @param {Grid} this
51220          * @param {Roo.GridDD} dd The drag drop object
51221          * @param {String} targetId The target drag drop object
51222          * @param {event} e The raw browser event
51223          */
51224         "dragenter" : true,
51225         /**
51226          * @event dragout
51227          * Fires when the dragged row(s) leave another DD target while being dragged
51228          * @param {Grid} this
51229          * @param {Roo.GridDD} dd The drag drop object
51230          * @param {String} targetId The target drag drop object
51231          * @param {event} e The raw browser event
51232          */
51233         "dragout" : true,
51234         /**
51235          * @event rowclass
51236          * Fires when a row is rendered, so you can change add a style to it.
51237          * @param {GridView} gridview   The grid view
51238          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51239          */
51240         'rowclass' : true,
51241
51242         /**
51243          * @event render
51244          * Fires when the grid is rendered
51245          * @param {Grid} grid
51246          */
51247         'render' : true
51248     });
51249
51250     Roo.grid.Grid.superclass.constructor.call(this);
51251 };
51252 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51253     
51254     /**
51255      * @cfg {String} ddGroup - drag drop group.
51256      */
51257
51258     /**
51259      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51260      */
51261     minColumnWidth : 25,
51262
51263     /**
51264      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51265      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51266      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51267      */
51268     autoSizeColumns : false,
51269
51270     /**
51271      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51272      */
51273     autoSizeHeaders : true,
51274
51275     /**
51276      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51277      */
51278     monitorWindowResize : true,
51279
51280     /**
51281      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51282      * rows measured to get a columns size. Default is 0 (all rows).
51283      */
51284     maxRowsToMeasure : 0,
51285
51286     /**
51287      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51288      */
51289     trackMouseOver : true,
51290
51291     /**
51292     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51293     */
51294     
51295     /**
51296     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51297     */
51298     enableDragDrop : false,
51299     
51300     /**
51301     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51302     */
51303     enableColumnMove : true,
51304     
51305     /**
51306     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51307     */
51308     enableColumnHide : true,
51309     
51310     /**
51311     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51312     */
51313     enableRowHeightSync : false,
51314     
51315     /**
51316     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51317     */
51318     stripeRows : true,
51319     
51320     /**
51321     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51322     */
51323     autoHeight : false,
51324
51325     /**
51326      * @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.
51327      */
51328     autoExpandColumn : false,
51329
51330     /**
51331     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51332     * Default is 50.
51333     */
51334     autoExpandMin : 50,
51335
51336     /**
51337     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51338     */
51339     autoExpandMax : 1000,
51340
51341     /**
51342     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51343     */
51344     view : null,
51345
51346     /**
51347     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51348     */
51349     loadMask : false,
51350     /**
51351     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51352     */
51353     dropTarget: false,
51354     
51355    
51356     
51357     // private
51358     rendered : false,
51359
51360     /**
51361     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51362     * of a fixed width. Default is false.
51363     */
51364     /**
51365     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51366     */
51367     /**
51368      * Called once after all setup has been completed and the grid is ready to be rendered.
51369      * @return {Roo.grid.Grid} this
51370      */
51371     render : function()
51372     {
51373         var c = this.container;
51374         // try to detect autoHeight/width mode
51375         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51376             this.autoHeight = true;
51377         }
51378         var view = this.getView();
51379         view.init(this);
51380
51381         c.on("click", this.onClick, this);
51382         c.on("dblclick", this.onDblClick, this);
51383         c.on("contextmenu", this.onContextMenu, this);
51384         c.on("keydown", this.onKeyDown, this);
51385         if (Roo.isTouch) {
51386             c.on("touchstart", this.onTouchStart, this);
51387         }
51388
51389         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51390
51391         this.getSelectionModel().init(this);
51392
51393         view.render();
51394
51395         if(this.loadMask){
51396             this.loadMask = new Roo.LoadMask(this.container,
51397                     Roo.apply({store:this.dataSource}, this.loadMask));
51398         }
51399         
51400         
51401         if (this.toolbar && this.toolbar.xtype) {
51402             this.toolbar.container = this.getView().getHeaderPanel(true);
51403             this.toolbar = new Roo.Toolbar(this.toolbar);
51404         }
51405         if (this.footer && this.footer.xtype) {
51406             this.footer.dataSource = this.getDataSource();
51407             this.footer.container = this.getView().getFooterPanel(true);
51408             this.footer = Roo.factory(this.footer, Roo);
51409         }
51410         if (this.dropTarget && this.dropTarget.xtype) {
51411             delete this.dropTarget.xtype;
51412             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51413         }
51414         
51415         
51416         this.rendered = true;
51417         this.fireEvent('render', this);
51418         return this;
51419     },
51420
51421         /**
51422          * Reconfigures the grid to use a different Store and Column Model.
51423          * The View will be bound to the new objects and refreshed.
51424          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51425          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51426          */
51427     reconfigure : function(dataSource, colModel){
51428         if(this.loadMask){
51429             this.loadMask.destroy();
51430             this.loadMask = new Roo.LoadMask(this.container,
51431                     Roo.apply({store:dataSource}, this.loadMask));
51432         }
51433         this.view.bind(dataSource, colModel);
51434         this.dataSource = dataSource;
51435         this.colModel = colModel;
51436         this.view.refresh(true);
51437     },
51438
51439     // private
51440     onKeyDown : function(e){
51441         this.fireEvent("keydown", e);
51442     },
51443
51444     /**
51445      * Destroy this grid.
51446      * @param {Boolean} removeEl True to remove the element
51447      */
51448     destroy : function(removeEl, keepListeners){
51449         if(this.loadMask){
51450             this.loadMask.destroy();
51451         }
51452         var c = this.container;
51453         c.removeAllListeners();
51454         this.view.destroy();
51455         this.colModel.purgeListeners();
51456         if(!keepListeners){
51457             this.purgeListeners();
51458         }
51459         c.update("");
51460         if(removeEl === true){
51461             c.remove();
51462         }
51463     },
51464
51465     // private
51466     processEvent : function(name, e){
51467         // does this fire select???
51468         Roo.log('grid:processEvent '  + name);
51469         
51470         if (name != 'touchstart' ) {
51471             this.fireEvent(name, e);    
51472         }
51473         
51474         var t = e.getTarget();
51475         var v = this.view;
51476         var header = v.findHeaderIndex(t);
51477         if(header !== false){
51478             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
51479         }else{
51480             var row = v.findRowIndex(t);
51481             var cell = v.findCellIndex(t);
51482             if (name == 'touchstart') {
51483                 // first touch is always a click.
51484                 // hopefull this happens after selection is updated.?
51485                 name = false;
51486                 
51487                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51488                     var cs = this.selModel.getSelectedCell();
51489                     if (row == cs[0] && cell == cs[1]){
51490                         name = 'dblclick';
51491                     }
51492                 }
51493                 if (typeof(this.selModel.getSelections) != 'undefined') {
51494                     var cs = this.selModel.getSelections();
51495                     var ds = this.dataSource;
51496                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51497                         name = 'dblclick';
51498                     }
51499                 }
51500                 if (!name) {
51501                     return;
51502                 }
51503             }
51504             
51505             
51506             if(row !== false){
51507                 this.fireEvent("row" + name, this, row, e);
51508                 if(cell !== false){
51509                     this.fireEvent("cell" + name, this, row, cell, e);
51510                 }
51511             }
51512         }
51513     },
51514
51515     // private
51516     onClick : function(e){
51517         this.processEvent("click", e);
51518     },
51519    // private
51520     onTouchStart : function(e){
51521         this.processEvent("touchstart", e);
51522     },
51523
51524     // private
51525     onContextMenu : function(e, t){
51526         this.processEvent("contextmenu", e);
51527     },
51528
51529     // private
51530     onDblClick : function(e){
51531         this.processEvent("dblclick", e);
51532     },
51533
51534     // private
51535     walkCells : function(row, col, step, fn, scope){
51536         var cm = this.colModel, clen = cm.getColumnCount();
51537         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51538         if(step < 0){
51539             if(col < 0){
51540                 row--;
51541                 first = false;
51542             }
51543             while(row >= 0){
51544                 if(!first){
51545                     col = clen-1;
51546                 }
51547                 first = false;
51548                 while(col >= 0){
51549                     if(fn.call(scope || this, row, col, cm) === true){
51550                         return [row, col];
51551                     }
51552                     col--;
51553                 }
51554                 row--;
51555             }
51556         } else {
51557             if(col >= clen){
51558                 row++;
51559                 first = false;
51560             }
51561             while(row < rlen){
51562                 if(!first){
51563                     col = 0;
51564                 }
51565                 first = false;
51566                 while(col < clen){
51567                     if(fn.call(scope || this, row, col, cm) === true){
51568                         return [row, col];
51569                     }
51570                     col++;
51571                 }
51572                 row++;
51573             }
51574         }
51575         return null;
51576     },
51577
51578     // private
51579     getSelections : function(){
51580         return this.selModel.getSelections();
51581     },
51582
51583     /**
51584      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51585      * but if manual update is required this method will initiate it.
51586      */
51587     autoSize : function(){
51588         if(this.rendered){
51589             this.view.layout();
51590             if(this.view.adjustForScroll){
51591                 this.view.adjustForScroll();
51592             }
51593         }
51594     },
51595
51596     /**
51597      * Returns the grid's underlying element.
51598      * @return {Element} The element
51599      */
51600     getGridEl : function(){
51601         return this.container;
51602     },
51603
51604     // private for compatibility, overridden by editor grid
51605     stopEditing : function(){},
51606
51607     /**
51608      * Returns the grid's SelectionModel.
51609      * @return {SelectionModel}
51610      */
51611     getSelectionModel : function(){
51612         if(!this.selModel){
51613             this.selModel = new Roo.grid.RowSelectionModel();
51614         }
51615         return this.selModel;
51616     },
51617
51618     /**
51619      * Returns the grid's DataSource.
51620      * @return {DataSource}
51621      */
51622     getDataSource : function(){
51623         return this.dataSource;
51624     },
51625
51626     /**
51627      * Returns the grid's ColumnModel.
51628      * @return {ColumnModel}
51629      */
51630     getColumnModel : function(){
51631         return this.colModel;
51632     },
51633
51634     /**
51635      * Returns the grid's GridView object.
51636      * @return {GridView}
51637      */
51638     getView : function(){
51639         if(!this.view){
51640             this.view = new Roo.grid.GridView(this.viewConfig);
51641         }
51642         return this.view;
51643     },
51644     /**
51645      * Called to get grid's drag proxy text, by default returns this.ddText.
51646      * @return {String}
51647      */
51648     getDragDropText : function(){
51649         var count = this.selModel.getCount();
51650         return String.format(this.ddText, count, count == 1 ? '' : 's');
51651     }
51652 });
51653 /**
51654  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51655  * %0 is replaced with the number of selected rows.
51656  * @type String
51657  */
51658 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51659  * Based on:
51660  * Ext JS Library 1.1.1
51661  * Copyright(c) 2006-2007, Ext JS, LLC.
51662  *
51663  * Originally Released Under LGPL - original licence link has changed is not relivant.
51664  *
51665  * Fork - LGPL
51666  * <script type="text/javascript">
51667  */
51668  
51669 Roo.grid.AbstractGridView = function(){
51670         this.grid = null;
51671         
51672         this.events = {
51673             "beforerowremoved" : true,
51674             "beforerowsinserted" : true,
51675             "beforerefresh" : true,
51676             "rowremoved" : true,
51677             "rowsinserted" : true,
51678             "rowupdated" : true,
51679             "refresh" : true
51680         };
51681     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51682 };
51683
51684 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51685     rowClass : "x-grid-row",
51686     cellClass : "x-grid-cell",
51687     tdClass : "x-grid-td",
51688     hdClass : "x-grid-hd",
51689     splitClass : "x-grid-hd-split",
51690     
51691         init: function(grid){
51692         this.grid = grid;
51693                 var cid = this.grid.getGridEl().id;
51694         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51695         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51696         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51697         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51698         },
51699         
51700         getColumnRenderers : function(){
51701         var renderers = [];
51702         var cm = this.grid.colModel;
51703         var colCount = cm.getColumnCount();
51704         for(var i = 0; i < colCount; i++){
51705             renderers[i] = cm.getRenderer(i);
51706         }
51707         return renderers;
51708     },
51709     
51710     getColumnIds : function(){
51711         var ids = [];
51712         var cm = this.grid.colModel;
51713         var colCount = cm.getColumnCount();
51714         for(var i = 0; i < colCount; i++){
51715             ids[i] = cm.getColumnId(i);
51716         }
51717         return ids;
51718     },
51719     
51720     getDataIndexes : function(){
51721         if(!this.indexMap){
51722             this.indexMap = this.buildIndexMap();
51723         }
51724         return this.indexMap.colToData;
51725     },
51726     
51727     getColumnIndexByDataIndex : function(dataIndex){
51728         if(!this.indexMap){
51729             this.indexMap = this.buildIndexMap();
51730         }
51731         return this.indexMap.dataToCol[dataIndex];
51732     },
51733     
51734     /**
51735      * Set a css style for a column dynamically. 
51736      * @param {Number} colIndex The index of the column
51737      * @param {String} name The css property name
51738      * @param {String} value The css value
51739      */
51740     setCSSStyle : function(colIndex, name, value){
51741         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51742         Roo.util.CSS.updateRule(selector, name, value);
51743     },
51744     
51745     generateRules : function(cm){
51746         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51747         Roo.util.CSS.removeStyleSheet(rulesId);
51748         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51749             var cid = cm.getColumnId(i);
51750             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51751                          this.tdSelector, cid, " {\n}\n",
51752                          this.hdSelector, cid, " {\n}\n",
51753                          this.splitSelector, cid, " {\n}\n");
51754         }
51755         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51756     }
51757 });/*
51758  * Based on:
51759  * Ext JS Library 1.1.1
51760  * Copyright(c) 2006-2007, Ext JS, LLC.
51761  *
51762  * Originally Released Under LGPL - original licence link has changed is not relivant.
51763  *
51764  * Fork - LGPL
51765  * <script type="text/javascript">
51766  */
51767
51768 // private
51769 // This is a support class used internally by the Grid components
51770 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51771     this.grid = grid;
51772     this.view = grid.getView();
51773     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51774     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51775     if(hd2){
51776         this.setHandleElId(Roo.id(hd));
51777         this.setOuterHandleElId(Roo.id(hd2));
51778     }
51779     this.scroll = false;
51780 };
51781 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51782     maxDragWidth: 120,
51783     getDragData : function(e){
51784         var t = Roo.lib.Event.getTarget(e);
51785         var h = this.view.findHeaderCell(t);
51786         if(h){
51787             return {ddel: h.firstChild, header:h};
51788         }
51789         return false;
51790     },
51791
51792     onInitDrag : function(e){
51793         this.view.headersDisabled = true;
51794         var clone = this.dragData.ddel.cloneNode(true);
51795         clone.id = Roo.id();
51796         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51797         this.proxy.update(clone);
51798         return true;
51799     },
51800
51801     afterValidDrop : function(){
51802         var v = this.view;
51803         setTimeout(function(){
51804             v.headersDisabled = false;
51805         }, 50);
51806     },
51807
51808     afterInvalidDrop : function(){
51809         var v = this.view;
51810         setTimeout(function(){
51811             v.headersDisabled = false;
51812         }, 50);
51813     }
51814 });
51815 /*
51816  * Based on:
51817  * Ext JS Library 1.1.1
51818  * Copyright(c) 2006-2007, Ext JS, LLC.
51819  *
51820  * Originally Released Under LGPL - original licence link has changed is not relivant.
51821  *
51822  * Fork - LGPL
51823  * <script type="text/javascript">
51824  */
51825 // private
51826 // This is a support class used internally by the Grid components
51827 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51828     this.grid = grid;
51829     this.view = grid.getView();
51830     // split the proxies so they don't interfere with mouse events
51831     this.proxyTop = Roo.DomHelper.append(document.body, {
51832         cls:"col-move-top", html:"&#160;"
51833     }, true);
51834     this.proxyBottom = Roo.DomHelper.append(document.body, {
51835         cls:"col-move-bottom", html:"&#160;"
51836     }, true);
51837     this.proxyTop.hide = this.proxyBottom.hide = function(){
51838         this.setLeftTop(-100,-100);
51839         this.setStyle("visibility", "hidden");
51840     };
51841     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51842     // temporarily disabled
51843     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51844     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51845 };
51846 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51847     proxyOffsets : [-4, -9],
51848     fly: Roo.Element.fly,
51849
51850     getTargetFromEvent : function(e){
51851         var t = Roo.lib.Event.getTarget(e);
51852         var cindex = this.view.findCellIndex(t);
51853         if(cindex !== false){
51854             return this.view.getHeaderCell(cindex);
51855         }
51856         return null;
51857     },
51858
51859     nextVisible : function(h){
51860         var v = this.view, cm = this.grid.colModel;
51861         h = h.nextSibling;
51862         while(h){
51863             if(!cm.isHidden(v.getCellIndex(h))){
51864                 return h;
51865             }
51866             h = h.nextSibling;
51867         }
51868         return null;
51869     },
51870
51871     prevVisible : function(h){
51872         var v = this.view, cm = this.grid.colModel;
51873         h = h.prevSibling;
51874         while(h){
51875             if(!cm.isHidden(v.getCellIndex(h))){
51876                 return h;
51877             }
51878             h = h.prevSibling;
51879         }
51880         return null;
51881     },
51882
51883     positionIndicator : function(h, n, e){
51884         var x = Roo.lib.Event.getPageX(e);
51885         var r = Roo.lib.Dom.getRegion(n.firstChild);
51886         var px, pt, py = r.top + this.proxyOffsets[1];
51887         if((r.right - x) <= (r.right-r.left)/2){
51888             px = r.right+this.view.borderWidth;
51889             pt = "after";
51890         }else{
51891             px = r.left;
51892             pt = "before";
51893         }
51894         var oldIndex = this.view.getCellIndex(h);
51895         var newIndex = this.view.getCellIndex(n);
51896
51897         if(this.grid.colModel.isFixed(newIndex)){
51898             return false;
51899         }
51900
51901         var locked = this.grid.colModel.isLocked(newIndex);
51902
51903         if(pt == "after"){
51904             newIndex++;
51905         }
51906         if(oldIndex < newIndex){
51907             newIndex--;
51908         }
51909         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51910             return false;
51911         }
51912         px +=  this.proxyOffsets[0];
51913         this.proxyTop.setLeftTop(px, py);
51914         this.proxyTop.show();
51915         if(!this.bottomOffset){
51916             this.bottomOffset = this.view.mainHd.getHeight();
51917         }
51918         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51919         this.proxyBottom.show();
51920         return pt;
51921     },
51922
51923     onNodeEnter : function(n, dd, e, data){
51924         if(data.header != n){
51925             this.positionIndicator(data.header, n, e);
51926         }
51927     },
51928
51929     onNodeOver : function(n, dd, e, data){
51930         var result = false;
51931         if(data.header != n){
51932             result = this.positionIndicator(data.header, n, e);
51933         }
51934         if(!result){
51935             this.proxyTop.hide();
51936             this.proxyBottom.hide();
51937         }
51938         return result ? this.dropAllowed : this.dropNotAllowed;
51939     },
51940
51941     onNodeOut : function(n, dd, e, data){
51942         this.proxyTop.hide();
51943         this.proxyBottom.hide();
51944     },
51945
51946     onNodeDrop : function(n, dd, e, data){
51947         var h = data.header;
51948         if(h != n){
51949             var cm = this.grid.colModel;
51950             var x = Roo.lib.Event.getPageX(e);
51951             var r = Roo.lib.Dom.getRegion(n.firstChild);
51952             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
51953             var oldIndex = this.view.getCellIndex(h);
51954             var newIndex = this.view.getCellIndex(n);
51955             var locked = cm.isLocked(newIndex);
51956             if(pt == "after"){
51957                 newIndex++;
51958             }
51959             if(oldIndex < newIndex){
51960                 newIndex--;
51961             }
51962             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
51963                 return false;
51964             }
51965             cm.setLocked(oldIndex, locked, true);
51966             cm.moveColumn(oldIndex, newIndex);
51967             this.grid.fireEvent("columnmove", oldIndex, newIndex);
51968             return true;
51969         }
51970         return false;
51971     }
51972 });
51973 /*
51974  * Based on:
51975  * Ext JS Library 1.1.1
51976  * Copyright(c) 2006-2007, Ext JS, LLC.
51977  *
51978  * Originally Released Under LGPL - original licence link has changed is not relivant.
51979  *
51980  * Fork - LGPL
51981  * <script type="text/javascript">
51982  */
51983   
51984 /**
51985  * @class Roo.grid.GridView
51986  * @extends Roo.util.Observable
51987  *
51988  * @constructor
51989  * @param {Object} config
51990  */
51991 Roo.grid.GridView = function(config){
51992     Roo.grid.GridView.superclass.constructor.call(this);
51993     this.el = null;
51994
51995     Roo.apply(this, config);
51996 };
51997
51998 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
51999
52000     unselectable :  'unselectable="on"',
52001     unselectableCls :  'x-unselectable',
52002     
52003     
52004     rowClass : "x-grid-row",
52005
52006     cellClass : "x-grid-col",
52007
52008     tdClass : "x-grid-td",
52009
52010     hdClass : "x-grid-hd",
52011
52012     splitClass : "x-grid-split",
52013
52014     sortClasses : ["sort-asc", "sort-desc"],
52015
52016     enableMoveAnim : false,
52017
52018     hlColor: "C3DAF9",
52019
52020     dh : Roo.DomHelper,
52021
52022     fly : Roo.Element.fly,
52023
52024     css : Roo.util.CSS,
52025
52026     borderWidth: 1,
52027
52028     splitOffset: 3,
52029
52030     scrollIncrement : 22,
52031
52032     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52033
52034     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52035
52036     bind : function(ds, cm){
52037         if(this.ds){
52038             this.ds.un("load", this.onLoad, this);
52039             this.ds.un("datachanged", this.onDataChange, this);
52040             this.ds.un("add", this.onAdd, this);
52041             this.ds.un("remove", this.onRemove, this);
52042             this.ds.un("update", this.onUpdate, this);
52043             this.ds.un("clear", this.onClear, this);
52044         }
52045         if(ds){
52046             ds.on("load", this.onLoad, this);
52047             ds.on("datachanged", this.onDataChange, this);
52048             ds.on("add", this.onAdd, this);
52049             ds.on("remove", this.onRemove, this);
52050             ds.on("update", this.onUpdate, this);
52051             ds.on("clear", this.onClear, this);
52052         }
52053         this.ds = ds;
52054
52055         if(this.cm){
52056             this.cm.un("widthchange", this.onColWidthChange, this);
52057             this.cm.un("headerchange", this.onHeaderChange, this);
52058             this.cm.un("hiddenchange", this.onHiddenChange, this);
52059             this.cm.un("columnmoved", this.onColumnMove, this);
52060             this.cm.un("columnlockchange", this.onColumnLock, this);
52061         }
52062         if(cm){
52063             this.generateRules(cm);
52064             cm.on("widthchange", this.onColWidthChange, this);
52065             cm.on("headerchange", this.onHeaderChange, this);
52066             cm.on("hiddenchange", this.onHiddenChange, this);
52067             cm.on("columnmoved", this.onColumnMove, this);
52068             cm.on("columnlockchange", this.onColumnLock, this);
52069         }
52070         this.cm = cm;
52071     },
52072
52073     init: function(grid){
52074         Roo.grid.GridView.superclass.init.call(this, grid);
52075
52076         this.bind(grid.dataSource, grid.colModel);
52077
52078         grid.on("headerclick", this.handleHeaderClick, this);
52079
52080         if(grid.trackMouseOver){
52081             grid.on("mouseover", this.onRowOver, this);
52082             grid.on("mouseout", this.onRowOut, this);
52083         }
52084         grid.cancelTextSelection = function(){};
52085         this.gridId = grid.id;
52086
52087         var tpls = this.templates || {};
52088
52089         if(!tpls.master){
52090             tpls.master = new Roo.Template(
52091                '<div class="x-grid" hidefocus="true">',
52092                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52093                   '<div class="x-grid-topbar"></div>',
52094                   '<div class="x-grid-scroller"><div></div></div>',
52095                   '<div class="x-grid-locked">',
52096                       '<div class="x-grid-header">{lockedHeader}</div>',
52097                       '<div class="x-grid-body">{lockedBody}</div>',
52098                   "</div>",
52099                   '<div class="x-grid-viewport">',
52100                       '<div class="x-grid-header">{header}</div>',
52101                       '<div class="x-grid-body">{body}</div>',
52102                   "</div>",
52103                   '<div class="x-grid-bottombar"></div>',
52104                  
52105                   '<div class="x-grid-resize-proxy">&#160;</div>',
52106                "</div>"
52107             );
52108             tpls.master.disableformats = true;
52109         }
52110
52111         if(!tpls.header){
52112             tpls.header = new Roo.Template(
52113                '<table border="0" cellspacing="0" cellpadding="0">',
52114                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52115                "</table>{splits}"
52116             );
52117             tpls.header.disableformats = true;
52118         }
52119         tpls.header.compile();
52120
52121         if(!tpls.hcell){
52122             tpls.hcell = new Roo.Template(
52123                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52124                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52125                 "</div></td>"
52126              );
52127              tpls.hcell.disableFormats = true;
52128         }
52129         tpls.hcell.compile();
52130
52131         if(!tpls.hsplit){
52132             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52133                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52134             tpls.hsplit.disableFormats = true;
52135         }
52136         tpls.hsplit.compile();
52137
52138         if(!tpls.body){
52139             tpls.body = new Roo.Template(
52140                '<table border="0" cellspacing="0" cellpadding="0">',
52141                "<tbody>{rows}</tbody>",
52142                "</table>"
52143             );
52144             tpls.body.disableFormats = true;
52145         }
52146         tpls.body.compile();
52147
52148         if(!tpls.row){
52149             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52150             tpls.row.disableFormats = true;
52151         }
52152         tpls.row.compile();
52153
52154         if(!tpls.cell){
52155             tpls.cell = new Roo.Template(
52156                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52157                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52158                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52159                 "</td>"
52160             );
52161             tpls.cell.disableFormats = true;
52162         }
52163         tpls.cell.compile();
52164
52165         this.templates = tpls;
52166     },
52167
52168     // remap these for backwards compat
52169     onColWidthChange : function(){
52170         this.updateColumns.apply(this, arguments);
52171     },
52172     onHeaderChange : function(){
52173         this.updateHeaders.apply(this, arguments);
52174     }, 
52175     onHiddenChange : function(){
52176         this.handleHiddenChange.apply(this, arguments);
52177     },
52178     onColumnMove : function(){
52179         this.handleColumnMove.apply(this, arguments);
52180     },
52181     onColumnLock : function(){
52182         this.handleLockChange.apply(this, arguments);
52183     },
52184
52185     onDataChange : function(){
52186         this.refresh();
52187         this.updateHeaderSortState();
52188     },
52189
52190     onClear : function(){
52191         this.refresh();
52192     },
52193
52194     onUpdate : function(ds, record){
52195         this.refreshRow(record);
52196     },
52197
52198     refreshRow : function(record){
52199         var ds = this.ds, index;
52200         if(typeof record == 'number'){
52201             index = record;
52202             record = ds.getAt(index);
52203         }else{
52204             index = ds.indexOf(record);
52205         }
52206         this.insertRows(ds, index, index, true);
52207         this.onRemove(ds, record, index+1, true);
52208         this.syncRowHeights(index, index);
52209         this.layout();
52210         this.fireEvent("rowupdated", this, index, record);
52211     },
52212
52213     onAdd : function(ds, records, index){
52214         this.insertRows(ds, index, index + (records.length-1));
52215     },
52216
52217     onRemove : function(ds, record, index, isUpdate){
52218         if(isUpdate !== true){
52219             this.fireEvent("beforerowremoved", this, index, record);
52220         }
52221         var bt = this.getBodyTable(), lt = this.getLockedTable();
52222         if(bt.rows[index]){
52223             bt.firstChild.removeChild(bt.rows[index]);
52224         }
52225         if(lt.rows[index]){
52226             lt.firstChild.removeChild(lt.rows[index]);
52227         }
52228         if(isUpdate !== true){
52229             this.stripeRows(index);
52230             this.syncRowHeights(index, index);
52231             this.layout();
52232             this.fireEvent("rowremoved", this, index, record);
52233         }
52234     },
52235
52236     onLoad : function(){
52237         this.scrollToTop();
52238     },
52239
52240     /**
52241      * Scrolls the grid to the top
52242      */
52243     scrollToTop : function(){
52244         if(this.scroller){
52245             this.scroller.dom.scrollTop = 0;
52246             this.syncScroll();
52247         }
52248     },
52249
52250     /**
52251      * Gets a panel in the header of the grid that can be used for toolbars etc.
52252      * After modifying the contents of this panel a call to grid.autoSize() may be
52253      * required to register any changes in size.
52254      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52255      * @return Roo.Element
52256      */
52257     getHeaderPanel : function(doShow){
52258         if(doShow){
52259             this.headerPanel.show();
52260         }
52261         return this.headerPanel;
52262     },
52263
52264     /**
52265      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52266      * After modifying the contents of this panel a call to grid.autoSize() may be
52267      * required to register any changes in size.
52268      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52269      * @return Roo.Element
52270      */
52271     getFooterPanel : function(doShow){
52272         if(doShow){
52273             this.footerPanel.show();
52274         }
52275         return this.footerPanel;
52276     },
52277
52278     initElements : function(){
52279         var E = Roo.Element;
52280         var el = this.grid.getGridEl().dom.firstChild;
52281         var cs = el.childNodes;
52282
52283         this.el = new E(el);
52284         
52285          this.focusEl = new E(el.firstChild);
52286         this.focusEl.swallowEvent("click", true);
52287         
52288         this.headerPanel = new E(cs[1]);
52289         this.headerPanel.enableDisplayMode("block");
52290
52291         this.scroller = new E(cs[2]);
52292         this.scrollSizer = new E(this.scroller.dom.firstChild);
52293
52294         this.lockedWrap = new E(cs[3]);
52295         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52296         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52297
52298         this.mainWrap = new E(cs[4]);
52299         this.mainHd = new E(this.mainWrap.dom.firstChild);
52300         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52301
52302         this.footerPanel = new E(cs[5]);
52303         this.footerPanel.enableDisplayMode("block");
52304
52305         this.resizeProxy = new E(cs[6]);
52306
52307         this.headerSelector = String.format(
52308            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52309            this.lockedHd.id, this.mainHd.id
52310         );
52311
52312         this.splitterSelector = String.format(
52313            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52314            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52315         );
52316     },
52317     idToCssName : function(s)
52318     {
52319         return s.replace(/[^a-z0-9]+/ig, '-');
52320     },
52321
52322     getHeaderCell : function(index){
52323         return Roo.DomQuery.select(this.headerSelector)[index];
52324     },
52325
52326     getHeaderCellMeasure : function(index){
52327         return this.getHeaderCell(index).firstChild;
52328     },
52329
52330     getHeaderCellText : function(index){
52331         return this.getHeaderCell(index).firstChild.firstChild;
52332     },
52333
52334     getLockedTable : function(){
52335         return this.lockedBody.dom.firstChild;
52336     },
52337
52338     getBodyTable : function(){
52339         return this.mainBody.dom.firstChild;
52340     },
52341
52342     getLockedRow : function(index){
52343         return this.getLockedTable().rows[index];
52344     },
52345
52346     getRow : function(index){
52347         return this.getBodyTable().rows[index];
52348     },
52349
52350     getRowComposite : function(index){
52351         if(!this.rowEl){
52352             this.rowEl = new Roo.CompositeElementLite();
52353         }
52354         var els = [], lrow, mrow;
52355         if(lrow = this.getLockedRow(index)){
52356             els.push(lrow);
52357         }
52358         if(mrow = this.getRow(index)){
52359             els.push(mrow);
52360         }
52361         this.rowEl.elements = els;
52362         return this.rowEl;
52363     },
52364     /**
52365      * Gets the 'td' of the cell
52366      * 
52367      * @param {Integer} rowIndex row to select
52368      * @param {Integer} colIndex column to select
52369      * 
52370      * @return {Object} 
52371      */
52372     getCell : function(rowIndex, colIndex){
52373         var locked = this.cm.getLockedCount();
52374         var source;
52375         if(colIndex < locked){
52376             source = this.lockedBody.dom.firstChild;
52377         }else{
52378             source = this.mainBody.dom.firstChild;
52379             colIndex -= locked;
52380         }
52381         return source.rows[rowIndex].childNodes[colIndex];
52382     },
52383
52384     getCellText : function(rowIndex, colIndex){
52385         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52386     },
52387
52388     getCellBox : function(cell){
52389         var b = this.fly(cell).getBox();
52390         if(Roo.isOpera){ // opera fails to report the Y
52391             b.y = cell.offsetTop + this.mainBody.getY();
52392         }
52393         return b;
52394     },
52395
52396     getCellIndex : function(cell){
52397         var id = String(cell.className).match(this.cellRE);
52398         if(id){
52399             return parseInt(id[1], 10);
52400         }
52401         return 0;
52402     },
52403
52404     findHeaderIndex : function(n){
52405         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52406         return r ? this.getCellIndex(r) : false;
52407     },
52408
52409     findHeaderCell : function(n){
52410         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52411         return r ? r : false;
52412     },
52413
52414     findRowIndex : function(n){
52415         if(!n){
52416             return false;
52417         }
52418         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52419         return r ? r.rowIndex : false;
52420     },
52421
52422     findCellIndex : function(node){
52423         var stop = this.el.dom;
52424         while(node && node != stop){
52425             if(this.findRE.test(node.className)){
52426                 return this.getCellIndex(node);
52427             }
52428             node = node.parentNode;
52429         }
52430         return false;
52431     },
52432
52433     getColumnId : function(index){
52434         return this.cm.getColumnId(index);
52435     },
52436
52437     getSplitters : function()
52438     {
52439         if(this.splitterSelector){
52440            return Roo.DomQuery.select(this.splitterSelector);
52441         }else{
52442             return null;
52443       }
52444     },
52445
52446     getSplitter : function(index){
52447         return this.getSplitters()[index];
52448     },
52449
52450     onRowOver : function(e, t){
52451         var row;
52452         if((row = this.findRowIndex(t)) !== false){
52453             this.getRowComposite(row).addClass("x-grid-row-over");
52454         }
52455     },
52456
52457     onRowOut : function(e, t){
52458         var row;
52459         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52460             this.getRowComposite(row).removeClass("x-grid-row-over");
52461         }
52462     },
52463
52464     renderHeaders : function(){
52465         var cm = this.cm;
52466         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52467         var cb = [], lb = [], sb = [], lsb = [], p = {};
52468         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52469             p.cellId = "x-grid-hd-0-" + i;
52470             p.splitId = "x-grid-csplit-0-" + i;
52471             p.id = cm.getColumnId(i);
52472             p.title = cm.getColumnTooltip(i) || "";
52473             p.value = cm.getColumnHeader(i) || "";
52474             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52475             if(!cm.isLocked(i)){
52476                 cb[cb.length] = ct.apply(p);
52477                 sb[sb.length] = st.apply(p);
52478             }else{
52479                 lb[lb.length] = ct.apply(p);
52480                 lsb[lsb.length] = st.apply(p);
52481             }
52482         }
52483         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52484                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52485     },
52486
52487     updateHeaders : function(){
52488         var html = this.renderHeaders();
52489         this.lockedHd.update(html[0]);
52490         this.mainHd.update(html[1]);
52491     },
52492
52493     /**
52494      * Focuses the specified row.
52495      * @param {Number} row The row index
52496      */
52497     focusRow : function(row)
52498     {
52499         //Roo.log('GridView.focusRow');
52500         var x = this.scroller.dom.scrollLeft;
52501         this.focusCell(row, 0, false);
52502         this.scroller.dom.scrollLeft = x;
52503     },
52504
52505     /**
52506      * Focuses the specified cell.
52507      * @param {Number} row The row index
52508      * @param {Number} col The column index
52509      * @param {Boolean} hscroll false to disable horizontal scrolling
52510      */
52511     focusCell : function(row, col, hscroll)
52512     {
52513         //Roo.log('GridView.focusCell');
52514         var el = this.ensureVisible(row, col, hscroll);
52515         this.focusEl.alignTo(el, "tl-tl");
52516         if(Roo.isGecko){
52517             this.focusEl.focus();
52518         }else{
52519             this.focusEl.focus.defer(1, this.focusEl);
52520         }
52521     },
52522
52523     /**
52524      * Scrolls the specified cell into view
52525      * @param {Number} row The row index
52526      * @param {Number} col The column index
52527      * @param {Boolean} hscroll false to disable horizontal scrolling
52528      */
52529     ensureVisible : function(row, col, hscroll)
52530     {
52531         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52532         //return null; //disable for testing.
52533         if(typeof row != "number"){
52534             row = row.rowIndex;
52535         }
52536         if(row < 0 && row >= this.ds.getCount()){
52537             return  null;
52538         }
52539         col = (col !== undefined ? col : 0);
52540         var cm = this.grid.colModel;
52541         while(cm.isHidden(col)){
52542             col++;
52543         }
52544
52545         var el = this.getCell(row, col);
52546         if(!el){
52547             return null;
52548         }
52549         var c = this.scroller.dom;
52550
52551         var ctop = parseInt(el.offsetTop, 10);
52552         var cleft = parseInt(el.offsetLeft, 10);
52553         var cbot = ctop + el.offsetHeight;
52554         var cright = cleft + el.offsetWidth;
52555         
52556         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52557         var stop = parseInt(c.scrollTop, 10);
52558         var sleft = parseInt(c.scrollLeft, 10);
52559         var sbot = stop + ch;
52560         var sright = sleft + c.clientWidth;
52561         /*
52562         Roo.log('GridView.ensureVisible:' +
52563                 ' ctop:' + ctop +
52564                 ' c.clientHeight:' + c.clientHeight +
52565                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52566                 ' stop:' + stop +
52567                 ' cbot:' + cbot +
52568                 ' sbot:' + sbot +
52569                 ' ch:' + ch  
52570                 );
52571         */
52572         if(ctop < stop){
52573              c.scrollTop = ctop;
52574             //Roo.log("set scrolltop to ctop DISABLE?");
52575         }else if(cbot > sbot){
52576             //Roo.log("set scrolltop to cbot-ch");
52577             c.scrollTop = cbot-ch;
52578         }
52579         
52580         if(hscroll !== false){
52581             if(cleft < sleft){
52582                 c.scrollLeft = cleft;
52583             }else if(cright > sright){
52584                 c.scrollLeft = cright-c.clientWidth;
52585             }
52586         }
52587          
52588         return el;
52589     },
52590
52591     updateColumns : function(){
52592         this.grid.stopEditing();
52593         var cm = this.grid.colModel, colIds = this.getColumnIds();
52594         //var totalWidth = cm.getTotalWidth();
52595         var pos = 0;
52596         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52597             //if(cm.isHidden(i)) continue;
52598             var w = cm.getColumnWidth(i);
52599             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52600             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52601         }
52602         this.updateSplitters();
52603     },
52604
52605     generateRules : function(cm){
52606         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52607         Roo.util.CSS.removeStyleSheet(rulesId);
52608         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52609             var cid = cm.getColumnId(i);
52610             var align = '';
52611             if(cm.config[i].align){
52612                 align = 'text-align:'+cm.config[i].align+';';
52613             }
52614             var hidden = '';
52615             if(cm.isHidden(i)){
52616                 hidden = 'display:none;';
52617             }
52618             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52619             ruleBuf.push(
52620                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52621                     this.hdSelector, cid, " {\n", align, width, "}\n",
52622                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52623                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52624         }
52625         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52626     },
52627
52628     updateSplitters : function(){
52629         var cm = this.cm, s = this.getSplitters();
52630         if(s){ // splitters not created yet
52631             var pos = 0, locked = true;
52632             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52633                 if(cm.isHidden(i)) continue;
52634                 var w = cm.getColumnWidth(i); // make sure it's a number
52635                 if(!cm.isLocked(i) && locked){
52636                     pos = 0;
52637                     locked = false;
52638                 }
52639                 pos += w;
52640                 s[i].style.left = (pos-this.splitOffset) + "px";
52641             }
52642         }
52643     },
52644
52645     handleHiddenChange : function(colModel, colIndex, hidden){
52646         if(hidden){
52647             this.hideColumn(colIndex);
52648         }else{
52649             this.unhideColumn(colIndex);
52650         }
52651     },
52652
52653     hideColumn : function(colIndex){
52654         var cid = this.getColumnId(colIndex);
52655         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52656         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52657         if(Roo.isSafari){
52658             this.updateHeaders();
52659         }
52660         this.updateSplitters();
52661         this.layout();
52662     },
52663
52664     unhideColumn : function(colIndex){
52665         var cid = this.getColumnId(colIndex);
52666         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52667         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52668
52669         if(Roo.isSafari){
52670             this.updateHeaders();
52671         }
52672         this.updateSplitters();
52673         this.layout();
52674     },
52675
52676     insertRows : function(dm, firstRow, lastRow, isUpdate){
52677         if(firstRow == 0 && lastRow == dm.getCount()-1){
52678             this.refresh();
52679         }else{
52680             if(!isUpdate){
52681                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52682             }
52683             var s = this.getScrollState();
52684             var markup = this.renderRows(firstRow, lastRow);
52685             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52686             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52687             this.restoreScroll(s);
52688             if(!isUpdate){
52689                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52690                 this.syncRowHeights(firstRow, lastRow);
52691                 this.stripeRows(firstRow);
52692                 this.layout();
52693             }
52694         }
52695     },
52696
52697     bufferRows : function(markup, target, index){
52698         var before = null, trows = target.rows, tbody = target.tBodies[0];
52699         if(index < trows.length){
52700             before = trows[index];
52701         }
52702         var b = document.createElement("div");
52703         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52704         var rows = b.firstChild.rows;
52705         for(var i = 0, len = rows.length; i < len; i++){
52706             if(before){
52707                 tbody.insertBefore(rows[0], before);
52708             }else{
52709                 tbody.appendChild(rows[0]);
52710             }
52711         }
52712         b.innerHTML = "";
52713         b = null;
52714     },
52715
52716     deleteRows : function(dm, firstRow, lastRow){
52717         if(dm.getRowCount()<1){
52718             this.fireEvent("beforerefresh", this);
52719             this.mainBody.update("");
52720             this.lockedBody.update("");
52721             this.fireEvent("refresh", this);
52722         }else{
52723             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52724             var bt = this.getBodyTable();
52725             var tbody = bt.firstChild;
52726             var rows = bt.rows;
52727             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52728                 tbody.removeChild(rows[firstRow]);
52729             }
52730             this.stripeRows(firstRow);
52731             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52732         }
52733     },
52734
52735     updateRows : function(dataSource, firstRow, lastRow){
52736         var s = this.getScrollState();
52737         this.refresh();
52738         this.restoreScroll(s);
52739     },
52740
52741     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52742         if(!noRefresh){
52743            this.refresh();
52744         }
52745         this.updateHeaderSortState();
52746     },
52747
52748     getScrollState : function(){
52749         
52750         var sb = this.scroller.dom;
52751         return {left: sb.scrollLeft, top: sb.scrollTop};
52752     },
52753
52754     stripeRows : function(startRow){
52755         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52756             return;
52757         }
52758         startRow = startRow || 0;
52759         var rows = this.getBodyTable().rows;
52760         var lrows = this.getLockedTable().rows;
52761         var cls = ' x-grid-row-alt ';
52762         for(var i = startRow, len = rows.length; i < len; i++){
52763             var row = rows[i], lrow = lrows[i];
52764             var isAlt = ((i+1) % 2 == 0);
52765             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52766             if(isAlt == hasAlt){
52767                 continue;
52768             }
52769             if(isAlt){
52770                 row.className += " x-grid-row-alt";
52771             }else{
52772                 row.className = row.className.replace("x-grid-row-alt", "");
52773             }
52774             if(lrow){
52775                 lrow.className = row.className;
52776             }
52777         }
52778     },
52779
52780     restoreScroll : function(state){
52781         //Roo.log('GridView.restoreScroll');
52782         var sb = this.scroller.dom;
52783         sb.scrollLeft = state.left;
52784         sb.scrollTop = state.top;
52785         this.syncScroll();
52786     },
52787
52788     syncScroll : function(){
52789         //Roo.log('GridView.syncScroll');
52790         var sb = this.scroller.dom;
52791         var sh = this.mainHd.dom;
52792         var bs = this.mainBody.dom;
52793         var lv = this.lockedBody.dom;
52794         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52795         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52796     },
52797
52798     handleScroll : function(e){
52799         this.syncScroll();
52800         var sb = this.scroller.dom;
52801         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52802         e.stopEvent();
52803     },
52804
52805     handleWheel : function(e){
52806         var d = e.getWheelDelta();
52807         this.scroller.dom.scrollTop -= d*22;
52808         // set this here to prevent jumpy scrolling on large tables
52809         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52810         e.stopEvent();
52811     },
52812
52813     renderRows : function(startRow, endRow){
52814         // pull in all the crap needed to render rows
52815         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52816         var colCount = cm.getColumnCount();
52817
52818         if(ds.getCount() < 1){
52819             return ["", ""];
52820         }
52821
52822         // build a map for all the columns
52823         var cs = [];
52824         for(var i = 0; i < colCount; i++){
52825             var name = cm.getDataIndex(i);
52826             cs[i] = {
52827                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52828                 renderer : cm.getRenderer(i),
52829                 id : cm.getColumnId(i),
52830                 locked : cm.isLocked(i)
52831             };
52832         }
52833
52834         startRow = startRow || 0;
52835         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52836
52837         // records to render
52838         var rs = ds.getRange(startRow, endRow);
52839
52840         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52841     },
52842
52843     // As much as I hate to duplicate code, this was branched because FireFox really hates
52844     // [].join("") on strings. The performance difference was substantial enough to
52845     // branch this function
52846     doRender : Roo.isGecko ?
52847             function(cs, rs, ds, startRow, colCount, stripe){
52848                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52849                 // buffers
52850                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52851                 
52852                 var hasListener = this.grid.hasListener('rowclass');
52853                 var rowcfg = {};
52854                 for(var j = 0, len = rs.length; j < len; j++){
52855                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52856                     for(var i = 0; i < colCount; i++){
52857                         c = cs[i];
52858                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52859                         p.id = c.id;
52860                         p.css = p.attr = "";
52861                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52862                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52863                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52864                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52865                         }
52866                         var markup = ct.apply(p);
52867                         if(!c.locked){
52868                             cb+= markup;
52869                         }else{
52870                             lcb+= markup;
52871                         }
52872                     }
52873                     var alt = [];
52874                     if(stripe && ((rowIndex+1) % 2 == 0)){
52875                         alt.push("x-grid-row-alt")
52876                     }
52877                     if(r.dirty){
52878                         alt.push(  " x-grid-dirty-row");
52879                     }
52880                     rp.cells = lcb;
52881                     if(this.getRowClass){
52882                         alt.push(this.getRowClass(r, rowIndex));
52883                     }
52884                     if (hasListener) {
52885                         rowcfg = {
52886                              
52887                             record: r,
52888                             rowIndex : rowIndex,
52889                             rowClass : ''
52890                         }
52891                         this.grid.fireEvent('rowclass', this, rowcfg);
52892                         alt.push(rowcfg.rowClass);
52893                     }
52894                     rp.alt = alt.join(" ");
52895                     lbuf+= rt.apply(rp);
52896                     rp.cells = cb;
52897                     buf+=  rt.apply(rp);
52898                 }
52899                 return [lbuf, buf];
52900             } :
52901             function(cs, rs, ds, startRow, colCount, stripe){
52902                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52903                 // buffers
52904                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52905                 var hasListener = this.grid.hasListener('rowclass');
52906  
52907                 var rowcfg = {};
52908                 for(var j = 0, len = rs.length; j < len; j++){
52909                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52910                     for(var i = 0; i < colCount; i++){
52911                         c = cs[i];
52912                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52913                         p.id = c.id;
52914                         p.css = p.attr = "";
52915                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52916                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52917                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52918                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52919                         }
52920                         
52921                         var markup = ct.apply(p);
52922                         if(!c.locked){
52923                             cb[cb.length] = markup;
52924                         }else{
52925                             lcb[lcb.length] = markup;
52926                         }
52927                     }
52928                     var alt = [];
52929                     if(stripe && ((rowIndex+1) % 2 == 0)){
52930                         alt.push( "x-grid-row-alt");
52931                     }
52932                     if(r.dirty){
52933                         alt.push(" x-grid-dirty-row");
52934                     }
52935                     rp.cells = lcb;
52936                     if(this.getRowClass){
52937                         alt.push( this.getRowClass(r, rowIndex));
52938                     }
52939                     if (hasListener) {
52940                         rowcfg = {
52941                              
52942                             record: r,
52943                             rowIndex : rowIndex,
52944                             rowClass : ''
52945                         }
52946                         this.grid.fireEvent('rowclass', this, rowcfg);
52947                         alt.push(rowcfg.rowClass);
52948                     }
52949                     rp.alt = alt.join(" ");
52950                     rp.cells = lcb.join("");
52951                     lbuf[lbuf.length] = rt.apply(rp);
52952                     rp.cells = cb.join("");
52953                     buf[buf.length] =  rt.apply(rp);
52954                 }
52955                 return [lbuf.join(""), buf.join("")];
52956             },
52957
52958     renderBody : function(){
52959         var markup = this.renderRows();
52960         var bt = this.templates.body;
52961         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
52962     },
52963
52964     /**
52965      * Refreshes the grid
52966      * @param {Boolean} headersToo
52967      */
52968     refresh : function(headersToo){
52969         this.fireEvent("beforerefresh", this);
52970         this.grid.stopEditing();
52971         var result = this.renderBody();
52972         this.lockedBody.update(result[0]);
52973         this.mainBody.update(result[1]);
52974         if(headersToo === true){
52975             this.updateHeaders();
52976             this.updateColumns();
52977             this.updateSplitters();
52978             this.updateHeaderSortState();
52979         }
52980         this.syncRowHeights();
52981         this.layout();
52982         this.fireEvent("refresh", this);
52983     },
52984
52985     handleColumnMove : function(cm, oldIndex, newIndex){
52986         this.indexMap = null;
52987         var s = this.getScrollState();
52988         this.refresh(true);
52989         this.restoreScroll(s);
52990         this.afterMove(newIndex);
52991     },
52992
52993     afterMove : function(colIndex){
52994         if(this.enableMoveAnim && Roo.enableFx){
52995             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
52996         }
52997         // if multisort - fix sortOrder, and reload..
52998         if (this.grid.dataSource.multiSort) {
52999             // the we can call sort again..
53000             var dm = this.grid.dataSource;
53001             var cm = this.grid.colModel;
53002             var so = [];
53003             for(var i = 0; i < cm.config.length; i++ ) {
53004                 
53005                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53006                     continue; // dont' bother, it's not in sort list or being set.
53007                 }
53008                 
53009                 so.push(cm.config[i].dataIndex);
53010             };
53011             dm.sortOrder = so;
53012             dm.load(dm.lastOptions);
53013             
53014             
53015         }
53016         
53017     },
53018
53019     updateCell : function(dm, rowIndex, dataIndex){
53020         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53021         if(typeof colIndex == "undefined"){ // not present in grid
53022             return;
53023         }
53024         var cm = this.grid.colModel;
53025         var cell = this.getCell(rowIndex, colIndex);
53026         var cellText = this.getCellText(rowIndex, colIndex);
53027
53028         var p = {
53029             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53030             id : cm.getColumnId(colIndex),
53031             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53032         };
53033         var renderer = cm.getRenderer(colIndex);
53034         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53035         if(typeof val == "undefined" || val === "") val = "&#160;";
53036         cellText.innerHTML = val;
53037         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53038         this.syncRowHeights(rowIndex, rowIndex);
53039     },
53040
53041     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53042         var maxWidth = 0;
53043         if(this.grid.autoSizeHeaders){
53044             var h = this.getHeaderCellMeasure(colIndex);
53045             maxWidth = Math.max(maxWidth, h.scrollWidth);
53046         }
53047         var tb, index;
53048         if(this.cm.isLocked(colIndex)){
53049             tb = this.getLockedTable();
53050             index = colIndex;
53051         }else{
53052             tb = this.getBodyTable();
53053             index = colIndex - this.cm.getLockedCount();
53054         }
53055         if(tb && tb.rows){
53056             var rows = tb.rows;
53057             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53058             for(var i = 0; i < stopIndex; i++){
53059                 var cell = rows[i].childNodes[index].firstChild;
53060                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53061             }
53062         }
53063         return maxWidth + /*margin for error in IE*/ 5;
53064     },
53065     /**
53066      * Autofit a column to its content.
53067      * @param {Number} colIndex
53068      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53069      */
53070      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53071          if(this.cm.isHidden(colIndex)){
53072              return; // can't calc a hidden column
53073          }
53074         if(forceMinSize){
53075             var cid = this.cm.getColumnId(colIndex);
53076             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53077            if(this.grid.autoSizeHeaders){
53078                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53079            }
53080         }
53081         var newWidth = this.calcColumnWidth(colIndex);
53082         this.cm.setColumnWidth(colIndex,
53083             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53084         if(!suppressEvent){
53085             this.grid.fireEvent("columnresize", colIndex, newWidth);
53086         }
53087     },
53088
53089     /**
53090      * Autofits all columns to their content and then expands to fit any extra space in the grid
53091      */
53092      autoSizeColumns : function(){
53093         var cm = this.grid.colModel;
53094         var colCount = cm.getColumnCount();
53095         for(var i = 0; i < colCount; i++){
53096             this.autoSizeColumn(i, true, true);
53097         }
53098         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53099             this.fitColumns();
53100         }else{
53101             this.updateColumns();
53102             this.layout();
53103         }
53104     },
53105
53106     /**
53107      * Autofits all columns to the grid's width proportionate with their current size
53108      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53109      */
53110     fitColumns : function(reserveScrollSpace){
53111         var cm = this.grid.colModel;
53112         var colCount = cm.getColumnCount();
53113         var cols = [];
53114         var width = 0;
53115         var i, w;
53116         for (i = 0; i < colCount; i++){
53117             if(!cm.isHidden(i) && !cm.isFixed(i)){
53118                 w = cm.getColumnWidth(i);
53119                 cols.push(i);
53120                 cols.push(w);
53121                 width += w;
53122             }
53123         }
53124         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53125         if(reserveScrollSpace){
53126             avail -= 17;
53127         }
53128         var frac = (avail - cm.getTotalWidth())/width;
53129         while (cols.length){
53130             w = cols.pop();
53131             i = cols.pop();
53132             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53133         }
53134         this.updateColumns();
53135         this.layout();
53136     },
53137
53138     onRowSelect : function(rowIndex){
53139         var row = this.getRowComposite(rowIndex);
53140         row.addClass("x-grid-row-selected");
53141     },
53142
53143     onRowDeselect : function(rowIndex){
53144         var row = this.getRowComposite(rowIndex);
53145         row.removeClass("x-grid-row-selected");
53146     },
53147
53148     onCellSelect : function(row, col){
53149         var cell = this.getCell(row, col);
53150         if(cell){
53151             Roo.fly(cell).addClass("x-grid-cell-selected");
53152         }
53153     },
53154
53155     onCellDeselect : function(row, col){
53156         var cell = this.getCell(row, col);
53157         if(cell){
53158             Roo.fly(cell).removeClass("x-grid-cell-selected");
53159         }
53160     },
53161
53162     updateHeaderSortState : function(){
53163         
53164         // sort state can be single { field: xxx, direction : yyy}
53165         // or   { xxx=>ASC , yyy : DESC ..... }
53166         
53167         var mstate = {};
53168         if (!this.ds.multiSort) { 
53169             var state = this.ds.getSortState();
53170             if(!state){
53171                 return;
53172             }
53173             mstate[state.field] = state.direction;
53174             // FIXME... - this is not used here.. but might be elsewhere..
53175             this.sortState = state;
53176             
53177         } else {
53178             mstate = this.ds.sortToggle;
53179         }
53180         //remove existing sort classes..
53181         
53182         var sc = this.sortClasses;
53183         var hds = this.el.select(this.headerSelector).removeClass(sc);
53184         
53185         for(var f in mstate) {
53186         
53187             var sortColumn = this.cm.findColumnIndex(f);
53188             
53189             if(sortColumn != -1){
53190                 var sortDir = mstate[f];        
53191                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53192             }
53193         }
53194         
53195          
53196         
53197     },
53198
53199
53200     handleHeaderClick : function(g, index){
53201         if(this.headersDisabled){
53202             return;
53203         }
53204         var dm = g.dataSource, cm = g.colModel;
53205         if(!cm.isSortable(index)){
53206             return;
53207         }
53208         g.stopEditing();
53209         
53210         if (dm.multiSort) {
53211             // update the sortOrder
53212             var so = [];
53213             for(var i = 0; i < cm.config.length; i++ ) {
53214                 
53215                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53216                     continue; // dont' bother, it's not in sort list or being set.
53217                 }
53218                 
53219                 so.push(cm.config[i].dataIndex);
53220             };
53221             dm.sortOrder = so;
53222         }
53223         
53224         
53225         dm.sort(cm.getDataIndex(index));
53226     },
53227
53228
53229     destroy : function(){
53230         if(this.colMenu){
53231             this.colMenu.removeAll();
53232             Roo.menu.MenuMgr.unregister(this.colMenu);
53233             this.colMenu.getEl().remove();
53234             delete this.colMenu;
53235         }
53236         if(this.hmenu){
53237             this.hmenu.removeAll();
53238             Roo.menu.MenuMgr.unregister(this.hmenu);
53239             this.hmenu.getEl().remove();
53240             delete this.hmenu;
53241         }
53242         if(this.grid.enableColumnMove){
53243             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53244             if(dds){
53245                 for(var dd in dds){
53246                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53247                         var elid = dds[dd].dragElId;
53248                         dds[dd].unreg();
53249                         Roo.get(elid).remove();
53250                     } else if(dds[dd].config.isTarget){
53251                         dds[dd].proxyTop.remove();
53252                         dds[dd].proxyBottom.remove();
53253                         dds[dd].unreg();
53254                     }
53255                     if(Roo.dd.DDM.locationCache[dd]){
53256                         delete Roo.dd.DDM.locationCache[dd];
53257                     }
53258                 }
53259                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53260             }
53261         }
53262         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53263         this.bind(null, null);
53264         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53265     },
53266
53267     handleLockChange : function(){
53268         this.refresh(true);
53269     },
53270
53271     onDenyColumnLock : function(){
53272
53273     },
53274
53275     onDenyColumnHide : function(){
53276
53277     },
53278
53279     handleHdMenuClick : function(item){
53280         var index = this.hdCtxIndex;
53281         var cm = this.cm, ds = this.ds;
53282         switch(item.id){
53283             case "asc":
53284                 ds.sort(cm.getDataIndex(index), "ASC");
53285                 break;
53286             case "desc":
53287                 ds.sort(cm.getDataIndex(index), "DESC");
53288                 break;
53289             case "lock":
53290                 var lc = cm.getLockedCount();
53291                 if(cm.getColumnCount(true) <= lc+1){
53292                     this.onDenyColumnLock();
53293                     return;
53294                 }
53295                 if(lc != index){
53296                     cm.setLocked(index, true, true);
53297                     cm.moveColumn(index, lc);
53298                     this.grid.fireEvent("columnmove", index, lc);
53299                 }else{
53300                     cm.setLocked(index, true);
53301                 }
53302             break;
53303             case "unlock":
53304                 var lc = cm.getLockedCount();
53305                 if((lc-1) != index){
53306                     cm.setLocked(index, false, true);
53307                     cm.moveColumn(index, lc-1);
53308                     this.grid.fireEvent("columnmove", index, lc-1);
53309                 }else{
53310                     cm.setLocked(index, false);
53311                 }
53312             break;
53313             default:
53314                 index = cm.getIndexById(item.id.substr(4));
53315                 if(index != -1){
53316                     if(item.checked && cm.getColumnCount(true) <= 1){
53317                         this.onDenyColumnHide();
53318                         return false;
53319                     }
53320                     cm.setHidden(index, item.checked);
53321                 }
53322         }
53323         return true;
53324     },
53325
53326     beforeColMenuShow : function(){
53327         var cm = this.cm,  colCount = cm.getColumnCount();
53328         this.colMenu.removeAll();
53329         for(var i = 0; i < colCount; i++){
53330             this.colMenu.add(new Roo.menu.CheckItem({
53331                 id: "col-"+cm.getColumnId(i),
53332                 text: cm.getColumnHeader(i),
53333                 checked: !cm.isHidden(i),
53334                 hideOnClick:false
53335             }));
53336         }
53337     },
53338
53339     handleHdCtx : function(g, index, e){
53340         e.stopEvent();
53341         var hd = this.getHeaderCell(index);
53342         this.hdCtxIndex = index;
53343         var ms = this.hmenu.items, cm = this.cm;
53344         ms.get("asc").setDisabled(!cm.isSortable(index));
53345         ms.get("desc").setDisabled(!cm.isSortable(index));
53346         if(this.grid.enableColLock !== false){
53347             ms.get("lock").setDisabled(cm.isLocked(index));
53348             ms.get("unlock").setDisabled(!cm.isLocked(index));
53349         }
53350         this.hmenu.show(hd, "tl-bl");
53351     },
53352
53353     handleHdOver : function(e){
53354         var hd = this.findHeaderCell(e.getTarget());
53355         if(hd && !this.headersDisabled){
53356             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53357                this.fly(hd).addClass("x-grid-hd-over");
53358             }
53359         }
53360     },
53361
53362     handleHdOut : function(e){
53363         var hd = this.findHeaderCell(e.getTarget());
53364         if(hd){
53365             this.fly(hd).removeClass("x-grid-hd-over");
53366         }
53367     },
53368
53369     handleSplitDblClick : function(e, t){
53370         var i = this.getCellIndex(t);
53371         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53372             this.autoSizeColumn(i, true);
53373             this.layout();
53374         }
53375     },
53376
53377     render : function(){
53378
53379         var cm = this.cm;
53380         var colCount = cm.getColumnCount();
53381
53382         if(this.grid.monitorWindowResize === true){
53383             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53384         }
53385         var header = this.renderHeaders();
53386         var body = this.templates.body.apply({rows:""});
53387         var html = this.templates.master.apply({
53388             lockedBody: body,
53389             body: body,
53390             lockedHeader: header[0],
53391             header: header[1]
53392         });
53393
53394         //this.updateColumns();
53395
53396         this.grid.getGridEl().dom.innerHTML = html;
53397
53398         this.initElements();
53399         
53400         // a kludge to fix the random scolling effect in webkit
53401         this.el.on("scroll", function() {
53402             this.el.dom.scrollTop=0; // hopefully not recursive..
53403         },this);
53404
53405         this.scroller.on("scroll", this.handleScroll, this);
53406         this.lockedBody.on("mousewheel", this.handleWheel, this);
53407         this.mainBody.on("mousewheel", this.handleWheel, this);
53408
53409         this.mainHd.on("mouseover", this.handleHdOver, this);
53410         this.mainHd.on("mouseout", this.handleHdOut, this);
53411         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53412                 {delegate: "."+this.splitClass});
53413
53414         this.lockedHd.on("mouseover", this.handleHdOver, this);
53415         this.lockedHd.on("mouseout", this.handleHdOut, this);
53416         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53417                 {delegate: "."+this.splitClass});
53418
53419         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53420             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53421         }
53422
53423         this.updateSplitters();
53424
53425         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53426             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53427             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53428         }
53429
53430         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53431             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53432             this.hmenu.add(
53433                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53434                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53435             );
53436             if(this.grid.enableColLock !== false){
53437                 this.hmenu.add('-',
53438                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53439                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53440                 );
53441             }
53442             if(this.grid.enableColumnHide !== false){
53443
53444                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53445                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53446                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53447
53448                 this.hmenu.add('-',
53449                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53450                 );
53451             }
53452             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53453
53454             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53455         }
53456
53457         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53458             this.dd = new Roo.grid.GridDragZone(this.grid, {
53459                 ddGroup : this.grid.ddGroup || 'GridDD'
53460             });
53461             
53462         }
53463
53464         /*
53465         for(var i = 0; i < colCount; i++){
53466             if(cm.isHidden(i)){
53467                 this.hideColumn(i);
53468             }
53469             if(cm.config[i].align){
53470                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53471                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53472             }
53473         }*/
53474         
53475         this.updateHeaderSortState();
53476
53477         this.beforeInitialResize();
53478         this.layout(true);
53479
53480         // two part rendering gives faster view to the user
53481         this.renderPhase2.defer(1, this);
53482     },
53483
53484     renderPhase2 : function(){
53485         // render the rows now
53486         this.refresh();
53487         if(this.grid.autoSizeColumns){
53488             this.autoSizeColumns();
53489         }
53490     },
53491
53492     beforeInitialResize : function(){
53493
53494     },
53495
53496     onColumnSplitterMoved : function(i, w){
53497         this.userResized = true;
53498         var cm = this.grid.colModel;
53499         cm.setColumnWidth(i, w, true);
53500         var cid = cm.getColumnId(i);
53501         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53502         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53503         this.updateSplitters();
53504         this.layout();
53505         this.grid.fireEvent("columnresize", i, w);
53506     },
53507
53508     syncRowHeights : function(startIndex, endIndex){
53509         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53510             startIndex = startIndex || 0;
53511             var mrows = this.getBodyTable().rows;
53512             var lrows = this.getLockedTable().rows;
53513             var len = mrows.length-1;
53514             endIndex = Math.min(endIndex || len, len);
53515             for(var i = startIndex; i <= endIndex; i++){
53516                 var m = mrows[i], l = lrows[i];
53517                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53518                 m.style.height = l.style.height = h + "px";
53519             }
53520         }
53521     },
53522
53523     layout : function(initialRender, is2ndPass){
53524         var g = this.grid;
53525         var auto = g.autoHeight;
53526         var scrollOffset = 16;
53527         var c = g.getGridEl(), cm = this.cm,
53528                 expandCol = g.autoExpandColumn,
53529                 gv = this;
53530         //c.beginMeasure();
53531
53532         if(!c.dom.offsetWidth){ // display:none?
53533             if(initialRender){
53534                 this.lockedWrap.show();
53535                 this.mainWrap.show();
53536             }
53537             return;
53538         }
53539
53540         var hasLock = this.cm.isLocked(0);
53541
53542         var tbh = this.headerPanel.getHeight();
53543         var bbh = this.footerPanel.getHeight();
53544
53545         if(auto){
53546             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53547             var newHeight = ch + c.getBorderWidth("tb");
53548             if(g.maxHeight){
53549                 newHeight = Math.min(g.maxHeight, newHeight);
53550             }
53551             c.setHeight(newHeight);
53552         }
53553
53554         if(g.autoWidth){
53555             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53556         }
53557
53558         var s = this.scroller;
53559
53560         var csize = c.getSize(true);
53561
53562         this.el.setSize(csize.width, csize.height);
53563
53564         this.headerPanel.setWidth(csize.width);
53565         this.footerPanel.setWidth(csize.width);
53566
53567         var hdHeight = this.mainHd.getHeight();
53568         var vw = csize.width;
53569         var vh = csize.height - (tbh + bbh);
53570
53571         s.setSize(vw, vh);
53572
53573         var bt = this.getBodyTable();
53574         var ltWidth = hasLock ?
53575                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
53576
53577         var scrollHeight = bt.offsetHeight;
53578         var scrollWidth = ltWidth + bt.offsetWidth;
53579         var vscroll = false, hscroll = false;
53580
53581         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
53582
53583         var lw = this.lockedWrap, mw = this.mainWrap;
53584         var lb = this.lockedBody, mb = this.mainBody;
53585
53586         setTimeout(function(){
53587             var t = s.dom.offsetTop;
53588             var w = s.dom.clientWidth,
53589                 h = s.dom.clientHeight;
53590
53591             lw.setTop(t);
53592             lw.setSize(ltWidth, h);
53593
53594             mw.setLeftTop(ltWidth, t);
53595             mw.setSize(w-ltWidth, h);
53596
53597             lb.setHeight(h-hdHeight);
53598             mb.setHeight(h-hdHeight);
53599
53600             if(is2ndPass !== true && !gv.userResized && expandCol){
53601                 // high speed resize without full column calculation
53602                 
53603                 var ci = cm.getIndexById(expandCol);
53604                 if (ci < 0) {
53605                     ci = cm.findColumnIndex(expandCol);
53606                 }
53607                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53608                 var expandId = cm.getColumnId(ci);
53609                 var  tw = cm.getTotalWidth(false);
53610                 var currentWidth = cm.getColumnWidth(ci);
53611                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53612                 if(currentWidth != cw){
53613                     cm.setColumnWidth(ci, cw, true);
53614                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53615                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53616                     gv.updateSplitters();
53617                     gv.layout(false, true);
53618                 }
53619             }
53620
53621             if(initialRender){
53622                 lw.show();
53623                 mw.show();
53624             }
53625             //c.endMeasure();
53626         }, 10);
53627     },
53628
53629     onWindowResize : function(){
53630         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53631             return;
53632         }
53633         this.layout();
53634     },
53635
53636     appendFooter : function(parentEl){
53637         return null;
53638     },
53639
53640     sortAscText : "Sort Ascending",
53641     sortDescText : "Sort Descending",
53642     lockText : "Lock Column",
53643     unlockText : "Unlock Column",
53644     columnsText : "Columns"
53645 });
53646
53647
53648 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53649     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53650     this.proxy.el.addClass('x-grid3-col-dd');
53651 };
53652
53653 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53654     handleMouseDown : function(e){
53655
53656     },
53657
53658     callHandleMouseDown : function(e){
53659         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53660     }
53661 });
53662 /*
53663  * Based on:
53664  * Ext JS Library 1.1.1
53665  * Copyright(c) 2006-2007, Ext JS, LLC.
53666  *
53667  * Originally Released Under LGPL - original licence link has changed is not relivant.
53668  *
53669  * Fork - LGPL
53670  * <script type="text/javascript">
53671  */
53672  
53673 // private
53674 // This is a support class used internally by the Grid components
53675 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53676     this.grid = grid;
53677     this.view = grid.getView();
53678     this.proxy = this.view.resizeProxy;
53679     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53680         "gridSplitters" + this.grid.getGridEl().id, {
53681         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53682     });
53683     this.setHandleElId(Roo.id(hd));
53684     this.setOuterHandleElId(Roo.id(hd2));
53685     this.scroll = false;
53686 };
53687 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53688     fly: Roo.Element.fly,
53689
53690     b4StartDrag : function(x, y){
53691         this.view.headersDisabled = true;
53692         this.proxy.setHeight(this.view.mainWrap.getHeight());
53693         var w = this.cm.getColumnWidth(this.cellIndex);
53694         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53695         this.resetConstraints();
53696         this.setXConstraint(minw, 1000);
53697         this.setYConstraint(0, 0);
53698         this.minX = x - minw;
53699         this.maxX = x + 1000;
53700         this.startPos = x;
53701         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53702     },
53703
53704
53705     handleMouseDown : function(e){
53706         ev = Roo.EventObject.setEvent(e);
53707         var t = this.fly(ev.getTarget());
53708         if(t.hasClass("x-grid-split")){
53709             this.cellIndex = this.view.getCellIndex(t.dom);
53710             this.split = t.dom;
53711             this.cm = this.grid.colModel;
53712             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53713                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53714             }
53715         }
53716     },
53717
53718     endDrag : function(e){
53719         this.view.headersDisabled = false;
53720         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53721         var diff = endX - this.startPos;
53722         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53723     },
53724
53725     autoOffset : function(){
53726         this.setDelta(0,0);
53727     }
53728 });/*
53729  * Based on:
53730  * Ext JS Library 1.1.1
53731  * Copyright(c) 2006-2007, Ext JS, LLC.
53732  *
53733  * Originally Released Under LGPL - original licence link has changed is not relivant.
53734  *
53735  * Fork - LGPL
53736  * <script type="text/javascript">
53737  */
53738  
53739 // private
53740 // This is a support class used internally by the Grid components
53741 Roo.grid.GridDragZone = function(grid, config){
53742     this.view = grid.getView();
53743     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53744     if(this.view.lockedBody){
53745         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53746         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53747     }
53748     this.scroll = false;
53749     this.grid = grid;
53750     this.ddel = document.createElement('div');
53751     this.ddel.className = 'x-grid-dd-wrap';
53752 };
53753
53754 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53755     ddGroup : "GridDD",
53756
53757     getDragData : function(e){
53758         var t = Roo.lib.Event.getTarget(e);
53759         var rowIndex = this.view.findRowIndex(t);
53760         var sm = this.grid.selModel;
53761             
53762         //Roo.log(rowIndex);
53763         
53764         if (sm.getSelectedCell) {
53765             // cell selection..
53766             if (!sm.getSelectedCell()) {
53767                 return false;
53768             }
53769             if (rowIndex != sm.getSelectedCell()[0]) {
53770                 return false;
53771             }
53772         
53773         }
53774         
53775         if(rowIndex !== false){
53776             
53777             // if editorgrid.. 
53778             
53779             
53780             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53781                
53782             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53783               //  
53784             //}
53785             if (e.hasModifier()){
53786                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53787             }
53788             
53789             Roo.log("getDragData");
53790             
53791             return {
53792                 grid: this.grid,
53793                 ddel: this.ddel,
53794                 rowIndex: rowIndex,
53795                 selections:sm.getSelections ? sm.getSelections() : (
53796                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53797                 )
53798             };
53799         }
53800         return false;
53801     },
53802
53803     onInitDrag : function(e){
53804         var data = this.dragData;
53805         this.ddel.innerHTML = this.grid.getDragDropText();
53806         this.proxy.update(this.ddel);
53807         // fire start drag?
53808     },
53809
53810     afterRepair : function(){
53811         this.dragging = false;
53812     },
53813
53814     getRepairXY : function(e, data){
53815         return false;
53816     },
53817
53818     onEndDrag : function(data, e){
53819         // fire end drag?
53820     },
53821
53822     onValidDrop : function(dd, e, id){
53823         // fire drag drop?
53824         this.hideProxy();
53825     },
53826
53827     beforeInvalidDrop : function(e, id){
53828
53829     }
53830 });/*
53831  * Based on:
53832  * Ext JS Library 1.1.1
53833  * Copyright(c) 2006-2007, Ext JS, LLC.
53834  *
53835  * Originally Released Under LGPL - original licence link has changed is not relivant.
53836  *
53837  * Fork - LGPL
53838  * <script type="text/javascript">
53839  */
53840  
53841
53842 /**
53843  * @class Roo.grid.ColumnModel
53844  * @extends Roo.util.Observable
53845  * This is the default implementation of a ColumnModel used by the Grid. It defines
53846  * the columns in the grid.
53847  * <br>Usage:<br>
53848  <pre><code>
53849  var colModel = new Roo.grid.ColumnModel([
53850         {header: "Ticker", width: 60, sortable: true, locked: true},
53851         {header: "Company Name", width: 150, sortable: true},
53852         {header: "Market Cap.", width: 100, sortable: true},
53853         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53854         {header: "Employees", width: 100, sortable: true, resizable: false}
53855  ]);
53856  </code></pre>
53857  * <p>
53858  
53859  * The config options listed for this class are options which may appear in each
53860  * individual column definition.
53861  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53862  * @constructor
53863  * @param {Object} config An Array of column config objects. See this class's
53864  * config objects for details.
53865 */
53866 Roo.grid.ColumnModel = function(config){
53867         /**
53868      * The config passed into the constructor
53869      */
53870     this.config = config;
53871     this.lookup = {};
53872
53873     // if no id, create one
53874     // if the column does not have a dataIndex mapping,
53875     // map it to the order it is in the config
53876     for(var i = 0, len = config.length; i < len; i++){
53877         var c = config[i];
53878         if(typeof c.dataIndex == "undefined"){
53879             c.dataIndex = i;
53880         }
53881         if(typeof c.renderer == "string"){
53882             c.renderer = Roo.util.Format[c.renderer];
53883         }
53884         if(typeof c.id == "undefined"){
53885             c.id = Roo.id();
53886         }
53887         if(c.editor && c.editor.xtype){
53888             c.editor  = Roo.factory(c.editor, Roo.grid);
53889         }
53890         if(c.editor && c.editor.isFormField){
53891             c.editor = new Roo.grid.GridEditor(c.editor);
53892         }
53893         this.lookup[c.id] = c;
53894     }
53895
53896     /**
53897      * The width of columns which have no width specified (defaults to 100)
53898      * @type Number
53899      */
53900     this.defaultWidth = 100;
53901
53902     /**
53903      * Default sortable of columns which have no sortable specified (defaults to false)
53904      * @type Boolean
53905      */
53906     this.defaultSortable = false;
53907
53908     this.addEvents({
53909         /**
53910              * @event widthchange
53911              * Fires when the width of a column changes.
53912              * @param {ColumnModel} this
53913              * @param {Number} columnIndex The column index
53914              * @param {Number} newWidth The new width
53915              */
53916             "widthchange": true,
53917         /**
53918              * @event headerchange
53919              * Fires when the text of a header changes.
53920              * @param {ColumnModel} this
53921              * @param {Number} columnIndex The column index
53922              * @param {Number} newText The new header text
53923              */
53924             "headerchange": true,
53925         /**
53926              * @event hiddenchange
53927              * Fires when a column is hidden or "unhidden".
53928              * @param {ColumnModel} this
53929              * @param {Number} columnIndex The column index
53930              * @param {Boolean} hidden true if hidden, false otherwise
53931              */
53932             "hiddenchange": true,
53933             /**
53934          * @event columnmoved
53935          * Fires when a column is moved.
53936          * @param {ColumnModel} this
53937          * @param {Number} oldIndex
53938          * @param {Number} newIndex
53939          */
53940         "columnmoved" : true,
53941         /**
53942          * @event columlockchange
53943          * Fires when a column's locked state is changed
53944          * @param {ColumnModel} this
53945          * @param {Number} colIndex
53946          * @param {Boolean} locked true if locked
53947          */
53948         "columnlockchange" : true
53949     });
53950     Roo.grid.ColumnModel.superclass.constructor.call(this);
53951 };
53952 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
53953     /**
53954      * @cfg {String} header The header text to display in the Grid view.
53955      */
53956     /**
53957      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
53958      * {@link Roo.data.Record} definition from which to draw the column's value. If not
53959      * specified, the column's index is used as an index into the Record's data Array.
53960      */
53961     /**
53962      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
53963      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
53964      */
53965     /**
53966      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
53967      * Defaults to the value of the {@link #defaultSortable} property.
53968      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
53969      */
53970     /**
53971      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
53972      */
53973     /**
53974      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
53975      */
53976     /**
53977      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
53978      */
53979     /**
53980      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
53981      */
53982     /**
53983      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
53984      * given the cell's data value. See {@link #setRenderer}. If not specified, the
53985      * default renderer uses the raw data value.
53986      */
53987        /**
53988      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
53989      */
53990     /**
53991      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
53992      */
53993
53994     /**
53995      * Returns the id of the column at the specified index.
53996      * @param {Number} index The column index
53997      * @return {String} the id
53998      */
53999     getColumnId : function(index){
54000         return this.config[index].id;
54001     },
54002
54003     /**
54004      * Returns the column for a specified id.
54005      * @param {String} id The column id
54006      * @return {Object} the column
54007      */
54008     getColumnById : function(id){
54009         return this.lookup[id];
54010     },
54011
54012     
54013     /**
54014      * Returns the column for a specified dataIndex.
54015      * @param {String} dataIndex The column dataIndex
54016      * @return {Object|Boolean} the column or false if not found
54017      */
54018     getColumnByDataIndex: function(dataIndex){
54019         var index = this.findColumnIndex(dataIndex);
54020         return index > -1 ? this.config[index] : false;
54021     },
54022     
54023     /**
54024      * Returns the index for a specified column id.
54025      * @param {String} id The column id
54026      * @return {Number} the index, or -1 if not found
54027      */
54028     getIndexById : function(id){
54029         for(var i = 0, len = this.config.length; i < len; i++){
54030             if(this.config[i].id == id){
54031                 return i;
54032             }
54033         }
54034         return -1;
54035     },
54036     
54037     /**
54038      * Returns the index for a specified column dataIndex.
54039      * @param {String} dataIndex The column dataIndex
54040      * @return {Number} the index, or -1 if not found
54041      */
54042     
54043     findColumnIndex : function(dataIndex){
54044         for(var i = 0, len = this.config.length; i < len; i++){
54045             if(this.config[i].dataIndex == dataIndex){
54046                 return i;
54047             }
54048         }
54049         return -1;
54050     },
54051     
54052     
54053     moveColumn : function(oldIndex, newIndex){
54054         var c = this.config[oldIndex];
54055         this.config.splice(oldIndex, 1);
54056         this.config.splice(newIndex, 0, c);
54057         this.dataMap = null;
54058         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54059     },
54060
54061     isLocked : function(colIndex){
54062         return this.config[colIndex].locked === true;
54063     },
54064
54065     setLocked : function(colIndex, value, suppressEvent){
54066         if(this.isLocked(colIndex) == value){
54067             return;
54068         }
54069         this.config[colIndex].locked = value;
54070         if(!suppressEvent){
54071             this.fireEvent("columnlockchange", this, colIndex, value);
54072         }
54073     },
54074
54075     getTotalLockedWidth : function(){
54076         var totalWidth = 0;
54077         for(var i = 0; i < this.config.length; i++){
54078             if(this.isLocked(i) && !this.isHidden(i)){
54079                 this.totalWidth += this.getColumnWidth(i);
54080             }
54081         }
54082         return totalWidth;
54083     },
54084
54085     getLockedCount : function(){
54086         for(var i = 0, len = this.config.length; i < len; i++){
54087             if(!this.isLocked(i)){
54088                 return i;
54089             }
54090         }
54091     },
54092
54093     /**
54094      * Returns the number of columns.
54095      * @return {Number}
54096      */
54097     getColumnCount : function(visibleOnly){
54098         if(visibleOnly === true){
54099             var c = 0;
54100             for(var i = 0, len = this.config.length; i < len; i++){
54101                 if(!this.isHidden(i)){
54102                     c++;
54103                 }
54104             }
54105             return c;
54106         }
54107         return this.config.length;
54108     },
54109
54110     /**
54111      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54112      * @param {Function} fn
54113      * @param {Object} scope (optional)
54114      * @return {Array} result
54115      */
54116     getColumnsBy : function(fn, scope){
54117         var r = [];
54118         for(var i = 0, len = this.config.length; i < len; i++){
54119             var c = this.config[i];
54120             if(fn.call(scope||this, c, i) === true){
54121                 r[r.length] = c;
54122             }
54123         }
54124         return r;
54125     },
54126
54127     /**
54128      * Returns true if the specified column is sortable.
54129      * @param {Number} col The column index
54130      * @return {Boolean}
54131      */
54132     isSortable : function(col){
54133         if(typeof this.config[col].sortable == "undefined"){
54134             return this.defaultSortable;
54135         }
54136         return this.config[col].sortable;
54137     },
54138
54139     /**
54140      * Returns the rendering (formatting) function defined for the column.
54141      * @param {Number} col The column index.
54142      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54143      */
54144     getRenderer : function(col){
54145         if(!this.config[col].renderer){
54146             return Roo.grid.ColumnModel.defaultRenderer;
54147         }
54148         return this.config[col].renderer;
54149     },
54150
54151     /**
54152      * Sets the rendering (formatting) function for a column.
54153      * @param {Number} col The column index
54154      * @param {Function} fn The function to use to process the cell's raw data
54155      * to return HTML markup for the grid view. The render function is called with
54156      * the following parameters:<ul>
54157      * <li>Data value.</li>
54158      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54159      * <li>css A CSS style string to apply to the table cell.</li>
54160      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54161      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54162      * <li>Row index</li>
54163      * <li>Column index</li>
54164      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54165      */
54166     setRenderer : function(col, fn){
54167         this.config[col].renderer = fn;
54168     },
54169
54170     /**
54171      * Returns the width for the specified column.
54172      * @param {Number} col The column index
54173      * @return {Number}
54174      */
54175     getColumnWidth : function(col){
54176         return this.config[col].width * 1 || this.defaultWidth;
54177     },
54178
54179     /**
54180      * Sets the width for a column.
54181      * @param {Number} col The column index
54182      * @param {Number} width The new width
54183      */
54184     setColumnWidth : function(col, width, suppressEvent){
54185         this.config[col].width = width;
54186         this.totalWidth = null;
54187         if(!suppressEvent){
54188              this.fireEvent("widthchange", this, col, width);
54189         }
54190     },
54191
54192     /**
54193      * Returns the total width of all columns.
54194      * @param {Boolean} includeHidden True to include hidden column widths
54195      * @return {Number}
54196      */
54197     getTotalWidth : function(includeHidden){
54198         if(!this.totalWidth){
54199             this.totalWidth = 0;
54200             for(var i = 0, len = this.config.length; i < len; i++){
54201                 if(includeHidden || !this.isHidden(i)){
54202                     this.totalWidth += this.getColumnWidth(i);
54203                 }
54204             }
54205         }
54206         return this.totalWidth;
54207     },
54208
54209     /**
54210      * Returns the header for the specified column.
54211      * @param {Number} col The column index
54212      * @return {String}
54213      */
54214     getColumnHeader : function(col){
54215         return this.config[col].header;
54216     },
54217
54218     /**
54219      * Sets the header for a column.
54220      * @param {Number} col The column index
54221      * @param {String} header The new header
54222      */
54223     setColumnHeader : function(col, header){
54224         this.config[col].header = header;
54225         this.fireEvent("headerchange", this, col, header);
54226     },
54227
54228     /**
54229      * Returns the tooltip for the specified column.
54230      * @param {Number} col The column index
54231      * @return {String}
54232      */
54233     getColumnTooltip : function(col){
54234             return this.config[col].tooltip;
54235     },
54236     /**
54237      * Sets the tooltip for a column.
54238      * @param {Number} col The column index
54239      * @param {String} tooltip The new tooltip
54240      */
54241     setColumnTooltip : function(col, tooltip){
54242             this.config[col].tooltip = tooltip;
54243     },
54244
54245     /**
54246      * Returns the dataIndex for the specified column.
54247      * @param {Number} col The column index
54248      * @return {Number}
54249      */
54250     getDataIndex : function(col){
54251         return this.config[col].dataIndex;
54252     },
54253
54254     /**
54255      * Sets the dataIndex for a column.
54256      * @param {Number} col The column index
54257      * @param {Number} dataIndex The new dataIndex
54258      */
54259     setDataIndex : function(col, dataIndex){
54260         this.config[col].dataIndex = dataIndex;
54261     },
54262
54263     
54264     
54265     /**
54266      * Returns true if the cell is editable.
54267      * @param {Number} colIndex The column index
54268      * @param {Number} rowIndex The row index
54269      * @return {Boolean}
54270      */
54271     isCellEditable : function(colIndex, rowIndex){
54272         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54273     },
54274
54275     /**
54276      * Returns the editor defined for the cell/column.
54277      * return false or null to disable editing.
54278      * @param {Number} colIndex The column index
54279      * @param {Number} rowIndex The row index
54280      * @return {Object}
54281      */
54282     getCellEditor : function(colIndex, rowIndex){
54283         return this.config[colIndex].editor;
54284     },
54285
54286     /**
54287      * Sets if a column is editable.
54288      * @param {Number} col The column index
54289      * @param {Boolean} editable True if the column is editable
54290      */
54291     setEditable : function(col, editable){
54292         this.config[col].editable = editable;
54293     },
54294
54295
54296     /**
54297      * Returns true if the column is hidden.
54298      * @param {Number} colIndex The column index
54299      * @return {Boolean}
54300      */
54301     isHidden : function(colIndex){
54302         return this.config[colIndex].hidden;
54303     },
54304
54305
54306     /**
54307      * Returns true if the column width cannot be changed
54308      */
54309     isFixed : function(colIndex){
54310         return this.config[colIndex].fixed;
54311     },
54312
54313     /**
54314      * Returns true if the column can be resized
54315      * @return {Boolean}
54316      */
54317     isResizable : function(colIndex){
54318         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54319     },
54320     /**
54321      * Sets if a column is hidden.
54322      * @param {Number} colIndex The column index
54323      * @param {Boolean} hidden True if the column is hidden
54324      */
54325     setHidden : function(colIndex, hidden){
54326         this.config[colIndex].hidden = hidden;
54327         this.totalWidth = null;
54328         this.fireEvent("hiddenchange", this, colIndex, hidden);
54329     },
54330
54331     /**
54332      * Sets the editor for a column.
54333      * @param {Number} col The column index
54334      * @param {Object} editor The editor object
54335      */
54336     setEditor : function(col, editor){
54337         this.config[col].editor = editor;
54338     }
54339 });
54340
54341 Roo.grid.ColumnModel.defaultRenderer = function(value){
54342         if(typeof value == "string" && value.length < 1){
54343             return "&#160;";
54344         }
54345         return value;
54346 };
54347
54348 // Alias for backwards compatibility
54349 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54350 /*
54351  * Based on:
54352  * Ext JS Library 1.1.1
54353  * Copyright(c) 2006-2007, Ext JS, LLC.
54354  *
54355  * Originally Released Under LGPL - original licence link has changed is not relivant.
54356  *
54357  * Fork - LGPL
54358  * <script type="text/javascript">
54359  */
54360
54361 /**
54362  * @class Roo.grid.AbstractSelectionModel
54363  * @extends Roo.util.Observable
54364  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54365  * implemented by descendant classes.  This class should not be directly instantiated.
54366  * @constructor
54367  */
54368 Roo.grid.AbstractSelectionModel = function(){
54369     this.locked = false;
54370     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54371 };
54372
54373 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54374     /** @ignore Called by the grid automatically. Do not call directly. */
54375     init : function(grid){
54376         this.grid = grid;
54377         this.initEvents();
54378     },
54379
54380     /**
54381      * Locks the selections.
54382      */
54383     lock : function(){
54384         this.locked = true;
54385     },
54386
54387     /**
54388      * Unlocks the selections.
54389      */
54390     unlock : function(){
54391         this.locked = false;
54392     },
54393
54394     /**
54395      * Returns true if the selections are locked.
54396      * @return {Boolean}
54397      */
54398     isLocked : function(){
54399         return this.locked;
54400     }
54401 });/*
54402  * Based on:
54403  * Ext JS Library 1.1.1
54404  * Copyright(c) 2006-2007, Ext JS, LLC.
54405  *
54406  * Originally Released Under LGPL - original licence link has changed is not relivant.
54407  *
54408  * Fork - LGPL
54409  * <script type="text/javascript">
54410  */
54411 /**
54412  * @extends Roo.grid.AbstractSelectionModel
54413  * @class Roo.grid.RowSelectionModel
54414  * The default SelectionModel used by {@link Roo.grid.Grid}.
54415  * It supports multiple selections and keyboard selection/navigation. 
54416  * @constructor
54417  * @param {Object} config
54418  */
54419 Roo.grid.RowSelectionModel = function(config){
54420     Roo.apply(this, config);
54421     this.selections = new Roo.util.MixedCollection(false, function(o){
54422         return o.id;
54423     });
54424
54425     this.last = false;
54426     this.lastActive = false;
54427
54428     this.addEvents({
54429         /**
54430              * @event selectionchange
54431              * Fires when the selection changes
54432              * @param {SelectionModel} this
54433              */
54434             "selectionchange" : true,
54435         /**
54436              * @event afterselectionchange
54437              * Fires after the selection changes (eg. by key press or clicking)
54438              * @param {SelectionModel} this
54439              */
54440             "afterselectionchange" : true,
54441         /**
54442              * @event beforerowselect
54443              * Fires when a row is selected being selected, return false to cancel.
54444              * @param {SelectionModel} this
54445              * @param {Number} rowIndex The selected index
54446              * @param {Boolean} keepExisting False if other selections will be cleared
54447              */
54448             "beforerowselect" : true,
54449         /**
54450              * @event rowselect
54451              * Fires when a row is selected.
54452              * @param {SelectionModel} this
54453              * @param {Number} rowIndex The selected index
54454              * @param {Roo.data.Record} r The record
54455              */
54456             "rowselect" : true,
54457         /**
54458              * @event rowdeselect
54459              * Fires when a row is deselected.
54460              * @param {SelectionModel} this
54461              * @param {Number} rowIndex The selected index
54462              */
54463         "rowdeselect" : true
54464     });
54465     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54466     this.locked = false;
54467 };
54468
54469 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54470     /**
54471      * @cfg {Boolean} singleSelect
54472      * True to allow selection of only one row at a time (defaults to false)
54473      */
54474     singleSelect : false,
54475
54476     // private
54477     initEvents : function(){
54478
54479         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54480             this.grid.on("mousedown", this.handleMouseDown, this);
54481         }else{ // allow click to work like normal
54482             this.grid.on("rowclick", this.handleDragableRowClick, this);
54483         }
54484
54485         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54486             "up" : function(e){
54487                 if(!e.shiftKey){
54488                     this.selectPrevious(e.shiftKey);
54489                 }else if(this.last !== false && this.lastActive !== false){
54490                     var last = this.last;
54491                     this.selectRange(this.last,  this.lastActive-1);
54492                     this.grid.getView().focusRow(this.lastActive);
54493                     if(last !== false){
54494                         this.last = last;
54495                     }
54496                 }else{
54497                     this.selectFirstRow();
54498                 }
54499                 this.fireEvent("afterselectionchange", this);
54500             },
54501             "down" : function(e){
54502                 if(!e.shiftKey){
54503                     this.selectNext(e.shiftKey);
54504                 }else if(this.last !== false && this.lastActive !== false){
54505                     var last = this.last;
54506                     this.selectRange(this.last,  this.lastActive+1);
54507                     this.grid.getView().focusRow(this.lastActive);
54508                     if(last !== false){
54509                         this.last = last;
54510                     }
54511                 }else{
54512                     this.selectFirstRow();
54513                 }
54514                 this.fireEvent("afterselectionchange", this);
54515             },
54516             scope: this
54517         });
54518
54519         var view = this.grid.view;
54520         view.on("refresh", this.onRefresh, this);
54521         view.on("rowupdated", this.onRowUpdated, this);
54522         view.on("rowremoved", this.onRemove, this);
54523     },
54524
54525     // private
54526     onRefresh : function(){
54527         var ds = this.grid.dataSource, i, v = this.grid.view;
54528         var s = this.selections;
54529         s.each(function(r){
54530             if((i = ds.indexOfId(r.id)) != -1){
54531                 v.onRowSelect(i);
54532             }else{
54533                 s.remove(r);
54534             }
54535         });
54536     },
54537
54538     // private
54539     onRemove : function(v, index, r){
54540         this.selections.remove(r);
54541     },
54542
54543     // private
54544     onRowUpdated : function(v, index, r){
54545         if(this.isSelected(r)){
54546             v.onRowSelect(index);
54547         }
54548     },
54549
54550     /**
54551      * Select records.
54552      * @param {Array} records The records to select
54553      * @param {Boolean} keepExisting (optional) True to keep existing selections
54554      */
54555     selectRecords : function(records, keepExisting){
54556         if(!keepExisting){
54557             this.clearSelections();
54558         }
54559         var ds = this.grid.dataSource;
54560         for(var i = 0, len = records.length; i < len; i++){
54561             this.selectRow(ds.indexOf(records[i]), true);
54562         }
54563     },
54564
54565     /**
54566      * Gets the number of selected rows.
54567      * @return {Number}
54568      */
54569     getCount : function(){
54570         return this.selections.length;
54571     },
54572
54573     /**
54574      * Selects the first row in the grid.
54575      */
54576     selectFirstRow : function(){
54577         this.selectRow(0);
54578     },
54579
54580     /**
54581      * Select the last row.
54582      * @param {Boolean} keepExisting (optional) True to keep existing selections
54583      */
54584     selectLastRow : function(keepExisting){
54585         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
54586     },
54587
54588     /**
54589      * Selects the row immediately following the last selected row.
54590      * @param {Boolean} keepExisting (optional) True to keep existing selections
54591      */
54592     selectNext : function(keepExisting){
54593         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54594             this.selectRow(this.last+1, keepExisting);
54595             this.grid.getView().focusRow(this.last);
54596         }
54597     },
54598
54599     /**
54600      * Selects the row that precedes the last selected row.
54601      * @param {Boolean} keepExisting (optional) True to keep existing selections
54602      */
54603     selectPrevious : function(keepExisting){
54604         if(this.last){
54605             this.selectRow(this.last-1, keepExisting);
54606             this.grid.getView().focusRow(this.last);
54607         }
54608     },
54609
54610     /**
54611      * Returns the selected records
54612      * @return {Array} Array of selected records
54613      */
54614     getSelections : function(){
54615         return [].concat(this.selections.items);
54616     },
54617
54618     /**
54619      * Returns the first selected record.
54620      * @return {Record}
54621      */
54622     getSelected : function(){
54623         return this.selections.itemAt(0);
54624     },
54625
54626
54627     /**
54628      * Clears all selections.
54629      */
54630     clearSelections : function(fast){
54631         if(this.locked) return;
54632         if(fast !== true){
54633             var ds = this.grid.dataSource;
54634             var s = this.selections;
54635             s.each(function(r){
54636                 this.deselectRow(ds.indexOfId(r.id));
54637             }, this);
54638             s.clear();
54639         }else{
54640             this.selections.clear();
54641         }
54642         this.last = false;
54643     },
54644
54645
54646     /**
54647      * Selects all rows.
54648      */
54649     selectAll : function(){
54650         if(this.locked) return;
54651         this.selections.clear();
54652         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54653             this.selectRow(i, true);
54654         }
54655     },
54656
54657     /**
54658      * Returns True if there is a selection.
54659      * @return {Boolean}
54660      */
54661     hasSelection : function(){
54662         return this.selections.length > 0;
54663     },
54664
54665     /**
54666      * Returns True if the specified row is selected.
54667      * @param {Number/Record} record The record or index of the record to check
54668      * @return {Boolean}
54669      */
54670     isSelected : function(index){
54671         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54672         return (r && this.selections.key(r.id) ? true : false);
54673     },
54674
54675     /**
54676      * Returns True if the specified record id is selected.
54677      * @param {String} id The id of record to check
54678      * @return {Boolean}
54679      */
54680     isIdSelected : function(id){
54681         return (this.selections.key(id) ? true : false);
54682     },
54683
54684     // private
54685     handleMouseDown : function(e, t){
54686         var view = this.grid.getView(), rowIndex;
54687         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54688             return;
54689         };
54690         if(e.shiftKey && this.last !== false){
54691             var last = this.last;
54692             this.selectRange(last, rowIndex, e.ctrlKey);
54693             this.last = last; // reset the last
54694             view.focusRow(rowIndex);
54695         }else{
54696             var isSelected = this.isSelected(rowIndex);
54697             if(e.button !== 0 && isSelected){
54698                 view.focusRow(rowIndex);
54699             }else if(e.ctrlKey && isSelected){
54700                 this.deselectRow(rowIndex);
54701             }else if(!isSelected){
54702                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54703                 view.focusRow(rowIndex);
54704             }
54705         }
54706         this.fireEvent("afterselectionchange", this);
54707     },
54708     // private
54709     handleDragableRowClick :  function(grid, rowIndex, e) 
54710     {
54711         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54712             this.selectRow(rowIndex, false);
54713             grid.view.focusRow(rowIndex);
54714              this.fireEvent("afterselectionchange", this);
54715         }
54716     },
54717     
54718     /**
54719      * Selects multiple rows.
54720      * @param {Array} rows Array of the indexes of the row to select
54721      * @param {Boolean} keepExisting (optional) True to keep existing selections
54722      */
54723     selectRows : function(rows, keepExisting){
54724         if(!keepExisting){
54725             this.clearSelections();
54726         }
54727         for(var i = 0, len = rows.length; i < len; i++){
54728             this.selectRow(rows[i], true);
54729         }
54730     },
54731
54732     /**
54733      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54734      * @param {Number} startRow The index of the first row in the range
54735      * @param {Number} endRow The index of the last row in the range
54736      * @param {Boolean} keepExisting (optional) True to retain existing selections
54737      */
54738     selectRange : function(startRow, endRow, keepExisting){
54739         if(this.locked) return;
54740         if(!keepExisting){
54741             this.clearSelections();
54742         }
54743         if(startRow <= endRow){
54744             for(var i = startRow; i <= endRow; i++){
54745                 this.selectRow(i, true);
54746             }
54747         }else{
54748             for(var i = startRow; i >= endRow; i--){
54749                 this.selectRow(i, true);
54750             }
54751         }
54752     },
54753
54754     /**
54755      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54756      * @param {Number} startRow The index of the first row in the range
54757      * @param {Number} endRow The index of the last row in the range
54758      */
54759     deselectRange : function(startRow, endRow, preventViewNotify){
54760         if(this.locked) return;
54761         for(var i = startRow; i <= endRow; i++){
54762             this.deselectRow(i, preventViewNotify);
54763         }
54764     },
54765
54766     /**
54767      * Selects a row.
54768      * @param {Number} row The index of the row to select
54769      * @param {Boolean} keepExisting (optional) True to keep existing selections
54770      */
54771     selectRow : function(index, keepExisting, preventViewNotify){
54772         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54773         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54774             if(!keepExisting || this.singleSelect){
54775                 this.clearSelections();
54776             }
54777             var r = this.grid.dataSource.getAt(index);
54778             this.selections.add(r);
54779             this.last = this.lastActive = index;
54780             if(!preventViewNotify){
54781                 this.grid.getView().onRowSelect(index);
54782             }
54783             this.fireEvent("rowselect", this, index, r);
54784             this.fireEvent("selectionchange", this);
54785         }
54786     },
54787
54788     /**
54789      * Deselects a row.
54790      * @param {Number} row The index of the row to deselect
54791      */
54792     deselectRow : function(index, preventViewNotify){
54793         if(this.locked) return;
54794         if(this.last == index){
54795             this.last = false;
54796         }
54797         if(this.lastActive == index){
54798             this.lastActive = false;
54799         }
54800         var r = this.grid.dataSource.getAt(index);
54801         this.selections.remove(r);
54802         if(!preventViewNotify){
54803             this.grid.getView().onRowDeselect(index);
54804         }
54805         this.fireEvent("rowdeselect", this, index);
54806         this.fireEvent("selectionchange", this);
54807     },
54808
54809     // private
54810     restoreLast : function(){
54811         if(this._last){
54812             this.last = this._last;
54813         }
54814     },
54815
54816     // private
54817     acceptsNav : function(row, col, cm){
54818         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54819     },
54820
54821     // private
54822     onEditorKey : function(field, e){
54823         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54824         if(k == e.TAB){
54825             e.stopEvent();
54826             ed.completeEdit();
54827             if(e.shiftKey){
54828                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54829             }else{
54830                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54831             }
54832         }else if(k == e.ENTER && !e.ctrlKey){
54833             e.stopEvent();
54834             ed.completeEdit();
54835             if(e.shiftKey){
54836                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54837             }else{
54838                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54839             }
54840         }else if(k == e.ESC){
54841             ed.cancelEdit();
54842         }
54843         if(newCell){
54844             g.startEditing(newCell[0], newCell[1]);
54845         }
54846     }
54847 });/*
54848  * Based on:
54849  * Ext JS Library 1.1.1
54850  * Copyright(c) 2006-2007, Ext JS, LLC.
54851  *
54852  * Originally Released Under LGPL - original licence link has changed is not relivant.
54853  *
54854  * Fork - LGPL
54855  * <script type="text/javascript">
54856  */
54857 /**
54858  * @class Roo.grid.CellSelectionModel
54859  * @extends Roo.grid.AbstractSelectionModel
54860  * This class provides the basic implementation for cell selection in a grid.
54861  * @constructor
54862  * @param {Object} config The object containing the configuration of this model.
54863  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54864  */
54865 Roo.grid.CellSelectionModel = function(config){
54866     Roo.apply(this, config);
54867
54868     this.selection = null;
54869
54870     this.addEvents({
54871         /**
54872              * @event beforerowselect
54873              * Fires before a cell is selected.
54874              * @param {SelectionModel} this
54875              * @param {Number} rowIndex The selected row index
54876              * @param {Number} colIndex The selected cell index
54877              */
54878             "beforecellselect" : true,
54879         /**
54880              * @event cellselect
54881              * Fires when a cell is selected.
54882              * @param {SelectionModel} this
54883              * @param {Number} rowIndex The selected row index
54884              * @param {Number} colIndex The selected cell index
54885              */
54886             "cellselect" : true,
54887         /**
54888              * @event selectionchange
54889              * Fires when the active selection changes.
54890              * @param {SelectionModel} this
54891              * @param {Object} selection null for no selection or an object (o) with two properties
54892                 <ul>
54893                 <li>o.record: the record object for the row the selection is in</li>
54894                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54895                 </ul>
54896              */
54897             "selectionchange" : true,
54898         /**
54899              * @event tabend
54900              * Fires when the tab (or enter) was pressed on the last editable cell
54901              * You can use this to trigger add new row.
54902              * @param {SelectionModel} this
54903              */
54904             "tabend" : true,
54905          /**
54906              * @event beforeeditnext
54907              * Fires before the next editable sell is made active
54908              * You can use this to skip to another cell or fire the tabend
54909              *    if you set cell to false
54910              * @param {Object} eventdata object : { cell : [ row, col ] } 
54911              */
54912             "beforeeditnext" : true
54913     });
54914     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54915 };
54916
54917 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54918     
54919     enter_is_tab: false,
54920
54921     /** @ignore */
54922     initEvents : function(){
54923         this.grid.on("mousedown", this.handleMouseDown, this);
54924         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54925         var view = this.grid.view;
54926         view.on("refresh", this.onViewChange, this);
54927         view.on("rowupdated", this.onRowUpdated, this);
54928         view.on("beforerowremoved", this.clearSelections, this);
54929         view.on("beforerowsinserted", this.clearSelections, this);
54930         if(this.grid.isEditor){
54931             this.grid.on("beforeedit", this.beforeEdit,  this);
54932         }
54933     },
54934
54935         //private
54936     beforeEdit : function(e){
54937         this.select(e.row, e.column, false, true, e.record);
54938     },
54939
54940         //private
54941     onRowUpdated : function(v, index, r){
54942         if(this.selection && this.selection.record == r){
54943             v.onCellSelect(index, this.selection.cell[1]);
54944         }
54945     },
54946
54947         //private
54948     onViewChange : function(){
54949         this.clearSelections(true);
54950     },
54951
54952         /**
54953          * Returns the currently selected cell,.
54954          * @return {Array} The selected cell (row, column) or null if none selected.
54955          */
54956     getSelectedCell : function(){
54957         return this.selection ? this.selection.cell : null;
54958     },
54959
54960     /**
54961      * Clears all selections.
54962      * @param {Boolean} true to prevent the gridview from being notified about the change.
54963      */
54964     clearSelections : function(preventNotify){
54965         var s = this.selection;
54966         if(s){
54967             if(preventNotify !== true){
54968                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
54969             }
54970             this.selection = null;
54971             this.fireEvent("selectionchange", this, null);
54972         }
54973     },
54974
54975     /**
54976      * Returns true if there is a selection.
54977      * @return {Boolean}
54978      */
54979     hasSelection : function(){
54980         return this.selection ? true : false;
54981     },
54982
54983     /** @ignore */
54984     handleMouseDown : function(e, t){
54985         var v = this.grid.getView();
54986         if(this.isLocked()){
54987             return;
54988         };
54989         var row = v.findRowIndex(t);
54990         var cell = v.findCellIndex(t);
54991         if(row !== false && cell !== false){
54992             this.select(row, cell);
54993         }
54994     },
54995
54996     /**
54997      * Selects a cell.
54998      * @param {Number} rowIndex
54999      * @param {Number} collIndex
55000      */
55001     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55002         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55003             this.clearSelections();
55004             r = r || this.grid.dataSource.getAt(rowIndex);
55005             this.selection = {
55006                 record : r,
55007                 cell : [rowIndex, colIndex]
55008             };
55009             if(!preventViewNotify){
55010                 var v = this.grid.getView();
55011                 v.onCellSelect(rowIndex, colIndex);
55012                 if(preventFocus !== true){
55013                     v.focusCell(rowIndex, colIndex);
55014                 }
55015             }
55016             this.fireEvent("cellselect", this, rowIndex, colIndex);
55017             this.fireEvent("selectionchange", this, this.selection);
55018         }
55019     },
55020
55021         //private
55022     isSelectable : function(rowIndex, colIndex, cm){
55023         return !cm.isHidden(colIndex);
55024     },
55025
55026     /** @ignore */
55027     handleKeyDown : function(e){
55028         //Roo.log('Cell Sel Model handleKeyDown');
55029         if(!e.isNavKeyPress()){
55030             return;
55031         }
55032         var g = this.grid, s = this.selection;
55033         if(!s){
55034             e.stopEvent();
55035             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55036             if(cell){
55037                 this.select(cell[0], cell[1]);
55038             }
55039             return;
55040         }
55041         var sm = this;
55042         var walk = function(row, col, step){
55043             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55044         };
55045         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55046         var newCell;
55047
55048       
55049
55050         switch(k){
55051             case e.TAB:
55052                 // handled by onEditorKey
55053                 if (g.isEditor && g.editing) {
55054                     return;
55055                 }
55056                 if(e.shiftKey) {
55057                     newCell = walk(r, c-1, -1);
55058                 } else {
55059                     newCell = walk(r, c+1, 1);
55060                 }
55061                 break;
55062             
55063             case e.DOWN:
55064                newCell = walk(r+1, c, 1);
55065                 break;
55066             
55067             case e.UP:
55068                 newCell = walk(r-1, c, -1);
55069                 break;
55070             
55071             case e.RIGHT:
55072                 newCell = walk(r, c+1, 1);
55073                 break;
55074             
55075             case e.LEFT:
55076                 newCell = walk(r, c-1, -1);
55077                 break;
55078             
55079             case e.ENTER:
55080                 
55081                 if(g.isEditor && !g.editing){
55082                    g.startEditing(r, c);
55083                    e.stopEvent();
55084                    return;
55085                 }
55086                 
55087                 
55088              break;
55089         };
55090         if(newCell){
55091             this.select(newCell[0], newCell[1]);
55092             e.stopEvent();
55093             
55094         }
55095     },
55096
55097     acceptsNav : function(row, col, cm){
55098         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55099     },
55100     /**
55101      * Selects a cell.
55102      * @param {Number} field (not used) - as it's normally used as a listener
55103      * @param {Number} e - event - fake it by using
55104      *
55105      * var e = Roo.EventObjectImpl.prototype;
55106      * e.keyCode = e.TAB
55107      *
55108      * 
55109      */
55110     onEditorKey : function(field, e){
55111         
55112         var k = e.getKey(),
55113             newCell,
55114             g = this.grid,
55115             ed = g.activeEditor,
55116             forward = false;
55117         ///Roo.log('onEditorKey' + k);
55118         
55119         
55120         if (this.enter_is_tab && k == e.ENTER) {
55121             k = e.TAB;
55122         }
55123         
55124         if(k == e.TAB){
55125             if(e.shiftKey){
55126                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55127             }else{
55128                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55129                 forward = true;
55130             }
55131             
55132             e.stopEvent();
55133             
55134         } else if(k == e.ENTER &&  !e.ctrlKey){
55135             ed.completeEdit();
55136             e.stopEvent();
55137             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55138         
55139                 } else if(k == e.ESC){
55140             ed.cancelEdit();
55141         }
55142                 
55143         if (newCell) {
55144             var ecall = { cell : newCell, forward : forward };
55145             this.fireEvent('beforeeditnext', ecall );
55146             newCell = ecall.cell;
55147                         forward = ecall.forward;
55148         }
55149                 
55150         if(newCell){
55151             //Roo.log('next cell after edit');
55152             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55153         } else if (forward) {
55154             // tabbed past last
55155             this.fireEvent.defer(100, this, ['tabend',this]);
55156         }
55157     }
55158 });/*
55159  * Based on:
55160  * Ext JS Library 1.1.1
55161  * Copyright(c) 2006-2007, Ext JS, LLC.
55162  *
55163  * Originally Released Under LGPL - original licence link has changed is not relivant.
55164  *
55165  * Fork - LGPL
55166  * <script type="text/javascript">
55167  */
55168  
55169 /**
55170  * @class Roo.grid.EditorGrid
55171  * @extends Roo.grid.Grid
55172  * Class for creating and editable grid.
55173  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55174  * The container MUST have some type of size defined for the grid to fill. The container will be 
55175  * automatically set to position relative if it isn't already.
55176  * @param {Object} dataSource The data model to bind to
55177  * @param {Object} colModel The column model with info about this grid's columns
55178  */
55179 Roo.grid.EditorGrid = function(container, config){
55180     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55181     this.getGridEl().addClass("xedit-grid");
55182
55183     if(!this.selModel){
55184         this.selModel = new Roo.grid.CellSelectionModel();
55185     }
55186
55187     this.activeEditor = null;
55188
55189         this.addEvents({
55190             /**
55191              * @event beforeedit
55192              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55193              * <ul style="padding:5px;padding-left:16px;">
55194              * <li>grid - This grid</li>
55195              * <li>record - The record being edited</li>
55196              * <li>field - The field name being edited</li>
55197              * <li>value - The value for the field being edited.</li>
55198              * <li>row - The grid row index</li>
55199              * <li>column - The grid column index</li>
55200              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55201              * </ul>
55202              * @param {Object} e An edit event (see above for description)
55203              */
55204             "beforeedit" : true,
55205             /**
55206              * @event afteredit
55207              * Fires after a cell is edited. <br />
55208              * <ul style="padding:5px;padding-left:16px;">
55209              * <li>grid - This grid</li>
55210              * <li>record - The record being edited</li>
55211              * <li>field - The field name being edited</li>
55212              * <li>value - The value being set</li>
55213              * <li>originalValue - The original value for the field, before the edit.</li>
55214              * <li>row - The grid row index</li>
55215              * <li>column - The grid column index</li>
55216              * </ul>
55217              * @param {Object} e An edit event (see above for description)
55218              */
55219             "afteredit" : true,
55220             /**
55221              * @event validateedit
55222              * Fires after a cell is edited, but before the value is set in the record. 
55223          * You can use this to modify the value being set in the field, Return false
55224              * to cancel the change. The edit event object has the following properties <br />
55225              * <ul style="padding:5px;padding-left:16px;">
55226          * <li>editor - This editor</li>
55227              * <li>grid - This grid</li>
55228              * <li>record - The record being edited</li>
55229              * <li>field - The field name being edited</li>
55230              * <li>value - The value being set</li>
55231              * <li>originalValue - The original value for the field, before the edit.</li>
55232              * <li>row - The grid row index</li>
55233              * <li>column - The grid column index</li>
55234              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55235              * </ul>
55236              * @param {Object} e An edit event (see above for description)
55237              */
55238             "validateedit" : true
55239         });
55240     this.on("bodyscroll", this.stopEditing,  this);
55241     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55242 };
55243
55244 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55245     /**
55246      * @cfg {Number} clicksToEdit
55247      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55248      */
55249     clicksToEdit: 2,
55250
55251     // private
55252     isEditor : true,
55253     // private
55254     trackMouseOver: false, // causes very odd FF errors
55255
55256     onCellDblClick : function(g, row, col){
55257         this.startEditing(row, col);
55258     },
55259
55260     onEditComplete : function(ed, value, startValue){
55261         this.editing = false;
55262         this.activeEditor = null;
55263         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55264         var r = ed.record;
55265         var field = this.colModel.getDataIndex(ed.col);
55266         var e = {
55267             grid: this,
55268             record: r,
55269             field: field,
55270             originalValue: startValue,
55271             value: value,
55272             row: ed.row,
55273             column: ed.col,
55274             cancel:false,
55275             editor: ed
55276         };
55277         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55278         cell.show();
55279           
55280         if(String(value) !== String(startValue)){
55281             
55282             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55283                 r.set(field, e.value);
55284                 // if we are dealing with a combo box..
55285                 // then we also set the 'name' colum to be the displayField
55286                 if (ed.field.displayField && ed.field.name) {
55287                     r.set(ed.field.name, ed.field.el.dom.value);
55288                 }
55289                 
55290                 delete e.cancel; //?? why!!!
55291                 this.fireEvent("afteredit", e);
55292             }
55293         } else {
55294             this.fireEvent("afteredit", e); // always fire it!
55295         }
55296         this.view.focusCell(ed.row, ed.col);
55297     },
55298
55299     /**
55300      * Starts editing the specified for the specified row/column
55301      * @param {Number} rowIndex
55302      * @param {Number} colIndex
55303      */
55304     startEditing : function(row, col){
55305         this.stopEditing();
55306         if(this.colModel.isCellEditable(col, row)){
55307             this.view.ensureVisible(row, col, true);
55308           
55309             var r = this.dataSource.getAt(row);
55310             var field = this.colModel.getDataIndex(col);
55311             var cell = Roo.get(this.view.getCell(row,col));
55312             var e = {
55313                 grid: this,
55314                 record: r,
55315                 field: field,
55316                 value: r.data[field],
55317                 row: row,
55318                 column: col,
55319                 cancel:false 
55320             };
55321             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55322                 this.editing = true;
55323                 var ed = this.colModel.getCellEditor(col, row);
55324                 
55325                 if (!ed) {
55326                     return;
55327                 }
55328                 if(!ed.rendered){
55329                     ed.render(ed.parentEl || document.body);
55330                 }
55331                 ed.field.reset();
55332                
55333                 cell.hide();
55334                 
55335                 (function(){ // complex but required for focus issues in safari, ie and opera
55336                     ed.row = row;
55337                     ed.col = col;
55338                     ed.record = r;
55339                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55340                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55341                     this.activeEditor = ed;
55342                     var v = r.data[field];
55343                     ed.startEdit(this.view.getCell(row, col), v);
55344                     // combo's with 'displayField and name set
55345                     if (ed.field.displayField && ed.field.name) {
55346                         ed.field.el.dom.value = r.data[ed.field.name];
55347                     }
55348                     
55349                     
55350                 }).defer(50, this);
55351             }
55352         }
55353     },
55354         
55355     /**
55356      * Stops any active editing
55357      */
55358     stopEditing : function(){
55359         if(this.activeEditor){
55360             this.activeEditor.completeEdit();
55361         }
55362         this.activeEditor = null;
55363     },
55364         
55365          /**
55366      * Called to get grid's drag proxy text, by default returns this.ddText.
55367      * @return {String}
55368      */
55369     getDragDropText : function(){
55370         var count = this.selModel.getSelectedCell() ? 1 : 0;
55371         return String.format(this.ddText, count, count == 1 ? '' : 's');
55372     }
55373         
55374 });/*
55375  * Based on:
55376  * Ext JS Library 1.1.1
55377  * Copyright(c) 2006-2007, Ext JS, LLC.
55378  *
55379  * Originally Released Under LGPL - original licence link has changed is not relivant.
55380  *
55381  * Fork - LGPL
55382  * <script type="text/javascript">
55383  */
55384
55385 // private - not really -- you end up using it !
55386 // This is a support class used internally by the Grid components
55387
55388 /**
55389  * @class Roo.grid.GridEditor
55390  * @extends Roo.Editor
55391  * Class for creating and editable grid elements.
55392  * @param {Object} config any settings (must include field)
55393  */
55394 Roo.grid.GridEditor = function(field, config){
55395     if (!config && field.field) {
55396         config = field;
55397         field = Roo.factory(config.field, Roo.form);
55398     }
55399     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55400     field.monitorTab = false;
55401 };
55402
55403 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55404     
55405     /**
55406      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55407      */
55408     
55409     alignment: "tl-tl",
55410     autoSize: "width",
55411     hideEl : false,
55412     cls: "x-small-editor x-grid-editor",
55413     shim:false,
55414     shadow:"frame"
55415 });/*
55416  * Based on:
55417  * Ext JS Library 1.1.1
55418  * Copyright(c) 2006-2007, Ext JS, LLC.
55419  *
55420  * Originally Released Under LGPL - original licence link has changed is not relivant.
55421  *
55422  * Fork - LGPL
55423  * <script type="text/javascript">
55424  */
55425   
55426
55427   
55428 Roo.grid.PropertyRecord = Roo.data.Record.create([
55429     {name:'name',type:'string'},  'value'
55430 ]);
55431
55432
55433 Roo.grid.PropertyStore = function(grid, source){
55434     this.grid = grid;
55435     this.store = new Roo.data.Store({
55436         recordType : Roo.grid.PropertyRecord
55437     });
55438     this.store.on('update', this.onUpdate,  this);
55439     if(source){
55440         this.setSource(source);
55441     }
55442     Roo.grid.PropertyStore.superclass.constructor.call(this);
55443 };
55444
55445
55446
55447 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55448     setSource : function(o){
55449         this.source = o;
55450         this.store.removeAll();
55451         var data = [];
55452         for(var k in o){
55453             if(this.isEditableValue(o[k])){
55454                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55455             }
55456         }
55457         this.store.loadRecords({records: data}, {}, true);
55458     },
55459
55460     onUpdate : function(ds, record, type){
55461         if(type == Roo.data.Record.EDIT){
55462             var v = record.data['value'];
55463             var oldValue = record.modified['value'];
55464             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55465                 this.source[record.id] = v;
55466                 record.commit();
55467                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55468             }else{
55469                 record.reject();
55470             }
55471         }
55472     },
55473
55474     getProperty : function(row){
55475        return this.store.getAt(row);
55476     },
55477
55478     isEditableValue: function(val){
55479         if(val && val instanceof Date){
55480             return true;
55481         }else if(typeof val == 'object' || typeof val == 'function'){
55482             return false;
55483         }
55484         return true;
55485     },
55486
55487     setValue : function(prop, value){
55488         this.source[prop] = value;
55489         this.store.getById(prop).set('value', value);
55490     },
55491
55492     getSource : function(){
55493         return this.source;
55494     }
55495 });
55496
55497 Roo.grid.PropertyColumnModel = function(grid, store){
55498     this.grid = grid;
55499     var g = Roo.grid;
55500     g.PropertyColumnModel.superclass.constructor.call(this, [
55501         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55502         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55503     ]);
55504     this.store = store;
55505     this.bselect = Roo.DomHelper.append(document.body, {
55506         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55507             {tag: 'option', value: 'true', html: 'true'},
55508             {tag: 'option', value: 'false', html: 'false'}
55509         ]
55510     });
55511     Roo.id(this.bselect);
55512     var f = Roo.form;
55513     this.editors = {
55514         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55515         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55516         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55517         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55518         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55519     };
55520     this.renderCellDelegate = this.renderCell.createDelegate(this);
55521     this.renderPropDelegate = this.renderProp.createDelegate(this);
55522 };
55523
55524 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55525     
55526     
55527     nameText : 'Name',
55528     valueText : 'Value',
55529     
55530     dateFormat : 'm/j/Y',
55531     
55532     
55533     renderDate : function(dateVal){
55534         return dateVal.dateFormat(this.dateFormat);
55535     },
55536
55537     renderBool : function(bVal){
55538         return bVal ? 'true' : 'false';
55539     },
55540
55541     isCellEditable : function(colIndex, rowIndex){
55542         return colIndex == 1;
55543     },
55544
55545     getRenderer : function(col){
55546         return col == 1 ?
55547             this.renderCellDelegate : this.renderPropDelegate;
55548     },
55549
55550     renderProp : function(v){
55551         return this.getPropertyName(v);
55552     },
55553
55554     renderCell : function(val){
55555         var rv = val;
55556         if(val instanceof Date){
55557             rv = this.renderDate(val);
55558         }else if(typeof val == 'boolean'){
55559             rv = this.renderBool(val);
55560         }
55561         return Roo.util.Format.htmlEncode(rv);
55562     },
55563
55564     getPropertyName : function(name){
55565         var pn = this.grid.propertyNames;
55566         return pn && pn[name] ? pn[name] : name;
55567     },
55568
55569     getCellEditor : function(colIndex, rowIndex){
55570         var p = this.store.getProperty(rowIndex);
55571         var n = p.data['name'], val = p.data['value'];
55572         
55573         if(typeof(this.grid.customEditors[n]) == 'string'){
55574             return this.editors[this.grid.customEditors[n]];
55575         }
55576         if(typeof(this.grid.customEditors[n]) != 'undefined'){
55577             return this.grid.customEditors[n];
55578         }
55579         if(val instanceof Date){
55580             return this.editors['date'];
55581         }else if(typeof val == 'number'){
55582             return this.editors['number'];
55583         }else if(typeof val == 'boolean'){
55584             return this.editors['boolean'];
55585         }else{
55586             return this.editors['string'];
55587         }
55588     }
55589 });
55590
55591 /**
55592  * @class Roo.grid.PropertyGrid
55593  * @extends Roo.grid.EditorGrid
55594  * This class represents the  interface of a component based property grid control.
55595  * <br><br>Usage:<pre><code>
55596  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55597       
55598  });
55599  // set any options
55600  grid.render();
55601  * </code></pre>
55602   
55603  * @constructor
55604  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55605  * The container MUST have some type of size defined for the grid to fill. The container will be
55606  * automatically set to position relative if it isn't already.
55607  * @param {Object} config A config object that sets properties on this grid.
55608  */
55609 Roo.grid.PropertyGrid = function(container, config){
55610     config = config || {};
55611     var store = new Roo.grid.PropertyStore(this);
55612     this.store = store;
55613     var cm = new Roo.grid.PropertyColumnModel(this, store);
55614     store.store.sort('name', 'ASC');
55615     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55616         ds: store.store,
55617         cm: cm,
55618         enableColLock:false,
55619         enableColumnMove:false,
55620         stripeRows:false,
55621         trackMouseOver: false,
55622         clicksToEdit:1
55623     }, config));
55624     this.getGridEl().addClass('x-props-grid');
55625     this.lastEditRow = null;
55626     this.on('columnresize', this.onColumnResize, this);
55627     this.addEvents({
55628          /**
55629              * @event beforepropertychange
55630              * Fires before a property changes (return false to stop?)
55631              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55632              * @param {String} id Record Id
55633              * @param {String} newval New Value
55634          * @param {String} oldval Old Value
55635              */
55636         "beforepropertychange": true,
55637         /**
55638              * @event propertychange
55639              * Fires after a property changes
55640              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55641              * @param {String} id Record Id
55642              * @param {String} newval New Value
55643          * @param {String} oldval Old Value
55644              */
55645         "propertychange": true
55646     });
55647     this.customEditors = this.customEditors || {};
55648 };
55649 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55650     
55651      /**
55652      * @cfg {Object} customEditors map of colnames=> custom editors.
55653      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55654      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55655      * false disables editing of the field.
55656          */
55657     
55658       /**
55659      * @cfg {Object} propertyNames map of property Names to their displayed value
55660          */
55661     
55662     render : function(){
55663         Roo.grid.PropertyGrid.superclass.render.call(this);
55664         this.autoSize.defer(100, this);
55665     },
55666
55667     autoSize : function(){
55668         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55669         if(this.view){
55670             this.view.fitColumns();
55671         }
55672     },
55673
55674     onColumnResize : function(){
55675         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55676         this.autoSize();
55677     },
55678     /**
55679      * Sets the data for the Grid
55680      * accepts a Key => Value object of all the elements avaiable.
55681      * @param {Object} data  to appear in grid.
55682      */
55683     setSource : function(source){
55684         this.store.setSource(source);
55685         //this.autoSize();
55686     },
55687     /**
55688      * Gets all the data from the grid.
55689      * @return {Object} data  data stored in grid
55690      */
55691     getSource : function(){
55692         return this.store.getSource();
55693     }
55694 });/*
55695  * Based on:
55696  * Ext JS Library 1.1.1
55697  * Copyright(c) 2006-2007, Ext JS, LLC.
55698  *
55699  * Originally Released Under LGPL - original licence link has changed is not relivant.
55700  *
55701  * Fork - LGPL
55702  * <script type="text/javascript">
55703  */
55704  
55705 /**
55706  * @class Roo.LoadMask
55707  * A simple utility class for generically masking elements while loading data.  If the element being masked has
55708  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
55709  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
55710  * element's UpdateManager load indicator and will be destroyed after the initial load.
55711  * @constructor
55712  * Create a new LoadMask
55713  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
55714  * @param {Object} config The config object
55715  */
55716 Roo.LoadMask = function(el, config){
55717     this.el = Roo.get(el);
55718     Roo.apply(this, config);
55719     if(this.store){
55720         this.store.on('beforeload', this.onBeforeLoad, this);
55721         this.store.on('load', this.onLoad, this);
55722         this.store.on('loadexception', this.onLoadException, this);
55723         this.removeMask = false;
55724     }else{
55725         var um = this.el.getUpdateManager();
55726         um.showLoadIndicator = false; // disable the default indicator
55727         um.on('beforeupdate', this.onBeforeLoad, this);
55728         um.on('update', this.onLoad, this);
55729         um.on('failure', this.onLoad, this);
55730         this.removeMask = true;
55731     }
55732 };
55733
55734 Roo.LoadMask.prototype = {
55735     /**
55736      * @cfg {Boolean} removeMask
55737      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
55738      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
55739      */
55740     /**
55741      * @cfg {String} msg
55742      * The text to display in a centered loading message box (defaults to 'Loading...')
55743      */
55744     msg : 'Loading...',
55745     /**
55746      * @cfg {String} msgCls
55747      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
55748      */
55749     msgCls : 'x-mask-loading',
55750
55751     /**
55752      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
55753      * @type Boolean
55754      */
55755     disabled: false,
55756
55757     /**
55758      * Disables the mask to prevent it from being displayed
55759      */
55760     disable : function(){
55761        this.disabled = true;
55762     },
55763
55764     /**
55765      * Enables the mask so that it can be displayed
55766      */
55767     enable : function(){
55768         this.disabled = false;
55769     },
55770     
55771     onLoadException : function()
55772     {
55773         Roo.log(arguments);
55774         
55775         if (typeof(arguments[3]) != 'undefined') {
55776             Roo.MessageBox.alert("Error loading",arguments[3]);
55777         } 
55778         /*
55779         try {
55780             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55781                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55782             }   
55783         } catch(e) {
55784             
55785         }
55786         */
55787     
55788         
55789         
55790         this.el.unmask(this.removeMask);
55791     },
55792     // private
55793     onLoad : function()
55794     {
55795         this.el.unmask(this.removeMask);
55796     },
55797
55798     // private
55799     onBeforeLoad : function(){
55800         if(!this.disabled){
55801             this.el.mask(this.msg, this.msgCls);
55802         }
55803     },
55804
55805     // private
55806     destroy : function(){
55807         if(this.store){
55808             this.store.un('beforeload', this.onBeforeLoad, this);
55809             this.store.un('load', this.onLoad, this);
55810             this.store.un('loadexception', this.onLoadException, this);
55811         }else{
55812             var um = this.el.getUpdateManager();
55813             um.un('beforeupdate', this.onBeforeLoad, this);
55814             um.un('update', this.onLoad, this);
55815             um.un('failure', this.onLoad, this);
55816         }
55817     }
55818 };/*
55819  * Based on:
55820  * Ext JS Library 1.1.1
55821  * Copyright(c) 2006-2007, Ext JS, LLC.
55822  *
55823  * Originally Released Under LGPL - original licence link has changed is not relivant.
55824  *
55825  * Fork - LGPL
55826  * <script type="text/javascript">
55827  */
55828
55829
55830 /**
55831  * @class Roo.XTemplate
55832  * @extends Roo.Template
55833  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
55834 <pre><code>
55835 var t = new Roo.XTemplate(
55836         '&lt;select name="{name}"&gt;',
55837                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
55838         '&lt;/select&gt;'
55839 );
55840  
55841 // then append, applying the master template values
55842  </code></pre>
55843  *
55844  * Supported features:
55845  *
55846  *  Tags:
55847
55848 <pre><code>
55849       {a_variable} - output encoded.
55850       {a_variable.format:("Y-m-d")} - call a method on the variable
55851       {a_variable:raw} - unencoded output
55852       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
55853       {a_variable:this.method_on_template(...)} - call a method on the template object.
55854  
55855 </code></pre>
55856  *  The tpl tag:
55857 <pre><code>
55858         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
55859         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
55860         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
55861         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
55862   
55863         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
55864         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
55865 </code></pre>
55866  *      
55867  */
55868 Roo.XTemplate = function()
55869 {
55870     Roo.XTemplate.superclass.constructor.apply(this, arguments);
55871     if (this.html) {
55872         this.compile();
55873     }
55874 };
55875
55876
55877 Roo.extend(Roo.XTemplate, Roo.Template, {
55878
55879     /**
55880      * The various sub templates
55881      */
55882     tpls : false,
55883     /**
55884      *
55885      * basic tag replacing syntax
55886      * WORD:WORD()
55887      *
55888      * // you can fake an object call by doing this
55889      *  x.t:(test,tesT) 
55890      * 
55891      */
55892     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
55893
55894     /**
55895      * compile the template
55896      *
55897      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
55898      *
55899      */
55900     compile: function()
55901     {
55902         var s = this.html;
55903      
55904         s = ['<tpl>', s, '</tpl>'].join('');
55905     
55906         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
55907             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
55908             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
55909             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
55910             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
55911             m,
55912             id     = 0,
55913             tpls   = [];
55914     
55915         while(true == !!(m = s.match(re))){
55916             var forMatch   = m[0].match(nameRe),
55917                 ifMatch   = m[0].match(ifRe),
55918                 execMatch   = m[0].match(execRe),
55919                 namedMatch   = m[0].match(namedRe),
55920                 
55921                 exp  = null, 
55922                 fn   = null,
55923                 exec = null,
55924                 name = forMatch && forMatch[1] ? forMatch[1] : '';
55925                 
55926             if (ifMatch) {
55927                 // if - puts fn into test..
55928                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
55929                 if(exp){
55930                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
55931                 }
55932             }
55933             
55934             if (execMatch) {
55935                 // exec - calls a function... returns empty if true is  returned.
55936                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
55937                 if(exp){
55938                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
55939                 }
55940             }
55941             
55942             
55943             if (name) {
55944                 // for = 
55945                 switch(name){
55946                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
55947                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
55948                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
55949                 }
55950             }
55951             var uid = namedMatch ? namedMatch[1] : id;
55952             
55953             
55954             tpls.push({
55955                 id:     namedMatch ? namedMatch[1] : id,
55956                 target: name,
55957                 exec:   exec,
55958                 test:   fn,
55959                 body:   m[1] || ''
55960             });
55961             if (namedMatch) {
55962                 s = s.replace(m[0], '');
55963             } else { 
55964                 s = s.replace(m[0], '{xtpl'+ id + '}');
55965             }
55966             ++id;
55967         }
55968         this.tpls = [];
55969         for(var i = tpls.length-1; i >= 0; --i){
55970             this.compileTpl(tpls[i]);
55971             this.tpls[tpls[i].id] = tpls[i];
55972         }
55973         this.master = tpls[tpls.length-1];
55974         return this;
55975     },
55976     /**
55977      * same as applyTemplate, except it's done to one of the subTemplates
55978      * when using named templates, you can do:
55979      *
55980      * var str = pl.applySubTemplate('your-name', values);
55981      *
55982      * 
55983      * @param {Number} id of the template
55984      * @param {Object} values to apply to template
55985      * @param {Object} parent (normaly the instance of this object)
55986      */
55987     applySubTemplate : function(id, values, parent)
55988     {
55989         
55990         
55991         var t = this.tpls[id];
55992         
55993         
55994         try { 
55995             if(t.test && !t.test.call(this, values, parent)){
55996                 return '';
55997             }
55998         } catch(e) {
55999             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
56000             Roo.log(e.toString());
56001             Roo.log(t.test);
56002             return ''
56003         }
56004         try { 
56005             
56006             if(t.exec && t.exec.call(this, values, parent)){
56007                 return '';
56008             }
56009         } catch(e) {
56010             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
56011             Roo.log(e.toString());
56012             Roo.log(t.exec);
56013             return ''
56014         }
56015         try {
56016             var vs = t.target ? t.target.call(this, values, parent) : values;
56017             parent = t.target ? values : parent;
56018             if(t.target && vs instanceof Array){
56019                 var buf = [];
56020                 for(var i = 0, len = vs.length; i < len; i++){
56021                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
56022                 }
56023                 return buf.join('');
56024             }
56025             return t.compiled.call(this, vs, parent);
56026         } catch (e) {
56027             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
56028             Roo.log(e.toString());
56029             Roo.log(t.compiled);
56030             return '';
56031         }
56032     },
56033
56034     compileTpl : function(tpl)
56035     {
56036         var fm = Roo.util.Format;
56037         var useF = this.disableFormats !== true;
56038         var sep = Roo.isGecko ? "+" : ",";
56039         var undef = function(str) {
56040             Roo.log("Property not found :"  + str);
56041             return '';
56042         };
56043         
56044         var fn = function(m, name, format, args)
56045         {
56046             //Roo.log(arguments);
56047             args = args ? args.replace(/\\'/g,"'") : args;
56048             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
56049             if (typeof(format) == 'undefined') {
56050                 format= 'htmlEncode';
56051             }
56052             if (format == 'raw' ) {
56053                 format = false;
56054             }
56055             
56056             if(name.substr(0, 4) == 'xtpl'){
56057                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
56058             }
56059             
56060             // build an array of options to determine if value is undefined..
56061             
56062             // basically get 'xxxx.yyyy' then do
56063             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
56064             //    (function () { Roo.log("Property not found"); return ''; })() :
56065             //    ......
56066             
56067             var udef_ar = [];
56068             var lookfor = '';
56069             Roo.each(name.split('.'), function(st) {
56070                 lookfor += (lookfor.length ? '.': '') + st;
56071                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
56072             });
56073             
56074             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
56075             
56076             
56077             if(format && useF){
56078                 
56079                 args = args ? ',' + args : "";
56080                  
56081                 if(format.substr(0, 5) != "this."){
56082                     format = "fm." + format + '(';
56083                 }else{
56084                     format = 'this.call("'+ format.substr(5) + '", ';
56085                     args = ", values";
56086                 }
56087                 
56088                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
56089             }
56090              
56091             if (args.length) {
56092                 // called with xxyx.yuu:(test,test)
56093                 // change to ()
56094                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
56095             }
56096             // raw.. - :raw modifier..
56097             return "'"+ sep + udef_st  + name + ")"+sep+"'";
56098             
56099         };
56100         var body;
56101         // branched to use + in gecko and [].join() in others
56102         if(Roo.isGecko){
56103             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
56104                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
56105                     "';};};";
56106         }else{
56107             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
56108             body.push(tpl.body.replace(/(\r\n|\n)/g,
56109                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
56110             body.push("'].join('');};};");
56111             body = body.join('');
56112         }
56113         
56114         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
56115        
56116         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
56117         eval(body);
56118         
56119         return this;
56120     },
56121
56122     applyTemplate : function(values){
56123         return this.master.compiled.call(this, values, {});
56124         //var s = this.subs;
56125     },
56126
56127     apply : function(){
56128         return this.applyTemplate.apply(this, arguments);
56129     }
56130
56131  });
56132
56133 Roo.XTemplate.from = function(el){
56134     el = Roo.getDom(el);
56135     return new Roo.XTemplate(el.value || el.innerHTML);
56136 };