Roo/form/ComboBoxArray.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      * overide
40494      * 
40495      */
40496     isDirty : function() {
40497         if(this.disabled) {
40498             return false;
40499         }
40500         
40501         try {
40502             var d = Roo.decode(String(this.originalValue));
40503         } catch (e) {
40504             return String(this.getValue()) !== String(this.originalValue);
40505         }
40506         
40507         var originalValue = [];
40508         
40509         for (var i = 0; i < d.length; i++){
40510             originalValue.push(d[i][this.valueField]);
40511         }
40512         
40513         return String(this.getValue()) !== String(originalValue.join(','));
40514         
40515     }
40516     
40517 });/*
40518  * Based on:
40519  * Ext JS Library 1.1.1
40520  * Copyright(c) 2006-2007, Ext JS, LLC.
40521  *
40522  * Originally Released Under LGPL - original licence link has changed is not relivant.
40523  *
40524  * Fork - LGPL
40525  * <script type="text/javascript">
40526  */
40527 /**
40528  * @class Roo.form.Checkbox
40529  * @extends Roo.form.Field
40530  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40531  * @constructor
40532  * Creates a new Checkbox
40533  * @param {Object} config Configuration options
40534  */
40535 Roo.form.Checkbox = function(config){
40536     Roo.form.Checkbox.superclass.constructor.call(this, config);
40537     this.addEvents({
40538         /**
40539          * @event check
40540          * Fires when the checkbox is checked or unchecked.
40541              * @param {Roo.form.Checkbox} this This checkbox
40542              * @param {Boolean} checked The new checked value
40543              */
40544         check : true
40545     });
40546 };
40547
40548 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40549     /**
40550      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40551      */
40552     focusClass : undefined,
40553     /**
40554      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40555      */
40556     fieldClass: "x-form-field",
40557     /**
40558      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40559      */
40560     checked: false,
40561     /**
40562      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40563      * {tag: "input", type: "checkbox", autocomplete: "off"})
40564      */
40565     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40566     /**
40567      * @cfg {String} boxLabel The text that appears beside the checkbox
40568      */
40569     boxLabel : "",
40570     /**
40571      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40572      */  
40573     inputValue : '1',
40574     /**
40575      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40576      */
40577      valueOff: '0', // value when not checked..
40578
40579     actionMode : 'viewEl', 
40580     //
40581     // private
40582     itemCls : 'x-menu-check-item x-form-item',
40583     groupClass : 'x-menu-group-item',
40584     inputType : 'hidden',
40585     
40586     
40587     inSetChecked: false, // check that we are not calling self...
40588     
40589     inputElement: false, // real input element?
40590     basedOn: false, // ????
40591     
40592     isFormField: true, // not sure where this is needed!!!!
40593
40594     onResize : function(){
40595         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40596         if(!this.boxLabel){
40597             this.el.alignTo(this.wrap, 'c-c');
40598         }
40599     },
40600
40601     initEvents : function(){
40602         Roo.form.Checkbox.superclass.initEvents.call(this);
40603         this.el.on("click", this.onClick,  this);
40604         this.el.on("change", this.onClick,  this);
40605     },
40606
40607
40608     getResizeEl : function(){
40609         return this.wrap;
40610     },
40611
40612     getPositionEl : function(){
40613         return this.wrap;
40614     },
40615
40616     // private
40617     onRender : function(ct, position){
40618         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40619         /*
40620         if(this.inputValue !== undefined){
40621             this.el.dom.value = this.inputValue;
40622         }
40623         */
40624         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40625         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40626         var viewEl = this.wrap.createChild({ 
40627             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40628         this.viewEl = viewEl;   
40629         this.wrap.on('click', this.onClick,  this); 
40630         
40631         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40632         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40633         
40634         
40635         
40636         if(this.boxLabel){
40637             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40638         //    viewEl.on('click', this.onClick,  this); 
40639         }
40640         //if(this.checked){
40641             this.setChecked(this.checked);
40642         //}else{
40643             //this.checked = this.el.dom;
40644         //}
40645
40646     },
40647
40648     // private
40649     initValue : Roo.emptyFn,
40650
40651     /**
40652      * Returns the checked state of the checkbox.
40653      * @return {Boolean} True if checked, else false
40654      */
40655     getValue : function(){
40656         if(this.el){
40657             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40658         }
40659         return this.valueOff;
40660         
40661     },
40662
40663         // private
40664     onClick : function(){ 
40665         this.setChecked(!this.checked);
40666
40667         //if(this.el.dom.checked != this.checked){
40668         //    this.setValue(this.el.dom.checked);
40669        // }
40670     },
40671
40672     /**
40673      * Sets the checked state of the checkbox.
40674      * On is always based on a string comparison between inputValue and the param.
40675      * @param {Boolean/String} value - the value to set 
40676      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40677      */
40678     setValue : function(v,suppressEvent){
40679         
40680         
40681         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40682         //if(this.el && this.el.dom){
40683         //    this.el.dom.checked = this.checked;
40684         //    this.el.dom.defaultChecked = this.checked;
40685         //}
40686         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40687         //this.fireEvent("check", this, this.checked);
40688     },
40689     // private..
40690     setChecked : function(state,suppressEvent)
40691     {
40692         if (this.inSetChecked) {
40693             this.checked = state;
40694             return;
40695         }
40696         
40697     
40698         if(this.wrap){
40699             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40700         }
40701         this.checked = state;
40702         if(suppressEvent !== true){
40703             this.fireEvent('check', this, state);
40704         }
40705         this.inSetChecked = true;
40706         this.el.dom.value = state ? this.inputValue : this.valueOff;
40707         this.inSetChecked = false;
40708         
40709     },
40710     // handle setting of hidden value by some other method!!?!?
40711     setFromHidden: function()
40712     {
40713         if(!this.el){
40714             return;
40715         }
40716         //console.log("SET FROM HIDDEN");
40717         //alert('setFrom hidden');
40718         this.setValue(this.el.dom.value);
40719     },
40720     
40721     onDestroy : function()
40722     {
40723         if(this.viewEl){
40724             Roo.get(this.viewEl).remove();
40725         }
40726          
40727         Roo.form.Checkbox.superclass.onDestroy.call(this);
40728     }
40729
40730 });/*
40731  * Based on:
40732  * Ext JS Library 1.1.1
40733  * Copyright(c) 2006-2007, Ext JS, LLC.
40734  *
40735  * Originally Released Under LGPL - original licence link has changed is not relivant.
40736  *
40737  * Fork - LGPL
40738  * <script type="text/javascript">
40739  */
40740  
40741 /**
40742  * @class Roo.form.Radio
40743  * @extends Roo.form.Checkbox
40744  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40745  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40746  * @constructor
40747  * Creates a new Radio
40748  * @param {Object} config Configuration options
40749  */
40750 Roo.form.Radio = function(){
40751     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40752 };
40753 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40754     inputType: 'radio',
40755
40756     /**
40757      * If this radio is part of a group, it will return the selected value
40758      * @return {String}
40759      */
40760     getGroupValue : function(){
40761         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40762     },
40763     
40764     
40765     onRender : function(ct, position){
40766         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40767         
40768         if(this.inputValue !== undefined){
40769             this.el.dom.value = this.inputValue;
40770         }
40771          
40772         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40773         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40774         //var viewEl = this.wrap.createChild({ 
40775         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40776         //this.viewEl = viewEl;   
40777         //this.wrap.on('click', this.onClick,  this); 
40778         
40779         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40780         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40781         
40782         
40783         
40784         if(this.boxLabel){
40785             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40786         //    viewEl.on('click', this.onClick,  this); 
40787         }
40788          if(this.checked){
40789             this.el.dom.checked =   'checked' ;
40790         }
40791          
40792     } 
40793     
40794     
40795 });//<script type="text/javascript">
40796
40797 /*
40798  * Ext JS Library 1.1.1
40799  * Copyright(c) 2006-2007, Ext JS, LLC.
40800  * licensing@extjs.com
40801  * 
40802  * http://www.extjs.com/license
40803  */
40804  
40805  /*
40806   * 
40807   * Known bugs:
40808   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40809   * - IE ? - no idea how much works there.
40810   * 
40811   * 
40812   * 
40813   */
40814  
40815
40816 /**
40817  * @class Ext.form.HtmlEditor
40818  * @extends Ext.form.Field
40819  * Provides a lightweight HTML Editor component.
40820  *
40821  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40822  * 
40823  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40824  * supported by this editor.</b><br/><br/>
40825  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40826  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40827  */
40828 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40829       /**
40830      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40831      */
40832     toolbars : false,
40833     /**
40834      * @cfg {String} createLinkText The default text for the create link prompt
40835      */
40836     createLinkText : 'Please enter the URL for the link:',
40837     /**
40838      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40839      */
40840     defaultLinkValue : 'http:/'+'/',
40841    
40842      /**
40843      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40844      *                        Roo.resizable.
40845      */
40846     resizable : false,
40847      /**
40848      * @cfg {Number} height (in pixels)
40849      */   
40850     height: 300,
40851    /**
40852      * @cfg {Number} width (in pixels)
40853      */   
40854     width: 500,
40855     
40856     /**
40857      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40858      * 
40859      */
40860     stylesheets: false,
40861     
40862     // id of frame..
40863     frameId: false,
40864     
40865     // private properties
40866     validationEvent : false,
40867     deferHeight: true,
40868     initialized : false,
40869     activated : false,
40870     sourceEditMode : false,
40871     onFocus : Roo.emptyFn,
40872     iframePad:3,
40873     hideMode:'offsets',
40874     
40875     defaultAutoCreate : { // modified by initCompnoent..
40876         tag: "textarea",
40877         style:"width:500px;height:300px;",
40878         autocomplete: "off"
40879     },
40880
40881     // private
40882     initComponent : function(){
40883         this.addEvents({
40884             /**
40885              * @event initialize
40886              * Fires when the editor is fully initialized (including the iframe)
40887              * @param {HtmlEditor} this
40888              */
40889             initialize: true,
40890             /**
40891              * @event activate
40892              * Fires when the editor is first receives the focus. Any insertion must wait
40893              * until after this event.
40894              * @param {HtmlEditor} this
40895              */
40896             activate: true,
40897              /**
40898              * @event beforesync
40899              * Fires before the textarea is updated with content from the editor iframe. Return false
40900              * to cancel the sync.
40901              * @param {HtmlEditor} this
40902              * @param {String} html
40903              */
40904             beforesync: true,
40905              /**
40906              * @event beforepush
40907              * Fires before the iframe editor is updated with content from the textarea. Return false
40908              * to cancel the push.
40909              * @param {HtmlEditor} this
40910              * @param {String} html
40911              */
40912             beforepush: true,
40913              /**
40914              * @event sync
40915              * Fires when the textarea is updated with content from the editor iframe.
40916              * @param {HtmlEditor} this
40917              * @param {String} html
40918              */
40919             sync: true,
40920              /**
40921              * @event push
40922              * Fires when the iframe editor is updated with content from the textarea.
40923              * @param {HtmlEditor} this
40924              * @param {String} html
40925              */
40926             push: true,
40927              /**
40928              * @event editmodechange
40929              * Fires when the editor switches edit modes
40930              * @param {HtmlEditor} this
40931              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40932              */
40933             editmodechange: true,
40934             /**
40935              * @event editorevent
40936              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40937              * @param {HtmlEditor} this
40938              */
40939             editorevent: true
40940         });
40941         this.defaultAutoCreate =  {
40942             tag: "textarea",
40943             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40944             autocomplete: "off"
40945         };
40946     },
40947
40948     /**
40949      * Protected method that will not generally be called directly. It
40950      * is called when the editor creates its toolbar. Override this method if you need to
40951      * add custom toolbar buttons.
40952      * @param {HtmlEditor} editor
40953      */
40954     createToolbar : function(editor){
40955         if (!editor.toolbars || !editor.toolbars.length) {
40956             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40957         }
40958         
40959         for (var i =0 ; i < editor.toolbars.length;i++) {
40960             editor.toolbars[i] = Roo.factory(
40961                     typeof(editor.toolbars[i]) == 'string' ?
40962                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40963                 Roo.form.HtmlEditor);
40964             editor.toolbars[i].init(editor);
40965         }
40966          
40967         
40968     },
40969
40970     /**
40971      * Protected method that will not generally be called directly. It
40972      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40973      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40974      */
40975     getDocMarkup : function(){
40976         // body styles..
40977         var st = '';
40978         if (this.stylesheets === false) {
40979             
40980             Roo.get(document.head).select('style').each(function(node) {
40981                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40982             });
40983             
40984             Roo.get(document.head).select('link').each(function(node) { 
40985                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40986             });
40987             
40988         } else if (!this.stylesheets.length) {
40989                 // simple..
40990                 st = '<style type="text/css">' +
40991                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40992                    '</style>';
40993         } else {
40994             Roo.each(this.stylesheets, function(s) {
40995                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40996             });
40997             
40998         }
40999         
41000         st +=  '<style type="text/css">' +
41001             'IMG { cursor: pointer } ' +
41002         '</style>';
41003
41004         
41005         return '<html><head>' + st  +
41006             //<style type="text/css">' +
41007             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41008             //'</style>' +
41009             ' </head><body class="roo-htmleditor-body"></body></html>';
41010     },
41011
41012     // private
41013     onRender : function(ct, position)
41014     {
41015         var _t = this;
41016         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
41017         this.el.dom.style.border = '0 none';
41018         this.el.dom.setAttribute('tabIndex', -1);
41019         this.el.addClass('x-hidden');
41020         if(Roo.isIE){ // fix IE 1px bogus margin
41021             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41022         }
41023         this.wrap = this.el.wrap({
41024             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
41025         });
41026         
41027         if (this.resizable) {
41028             this.resizeEl = new Roo.Resizable(this.wrap, {
41029                 pinned : true,
41030                 wrap: true,
41031                 dynamic : true,
41032                 minHeight : this.height,
41033                 height: this.height,
41034                 handles : this.resizable,
41035                 width: this.width,
41036                 listeners : {
41037                     resize : function(r, w, h) {
41038                         _t.onResize(w,h); // -something
41039                     }
41040                 }
41041             });
41042             
41043         }
41044
41045         this.frameId = Roo.id();
41046         
41047         this.createToolbar(this);
41048         
41049       
41050         
41051         var iframe = this.wrap.createChild({
41052             tag: 'iframe',
41053             id: this.frameId,
41054             name: this.frameId,
41055             frameBorder : 'no',
41056             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41057         }, this.el
41058         );
41059         
41060        // console.log(iframe);
41061         //this.wrap.dom.appendChild(iframe);
41062
41063         this.iframe = iframe.dom;
41064
41065          this.assignDocWin();
41066         
41067         this.doc.designMode = 'on';
41068        
41069         this.doc.open();
41070         this.doc.write(this.getDocMarkup());
41071         this.doc.close();
41072
41073         
41074         var task = { // must defer to wait for browser to be ready
41075             run : function(){
41076                 //console.log("run task?" + this.doc.readyState);
41077                 this.assignDocWin();
41078                 if(this.doc.body || this.doc.readyState == 'complete'){
41079                     try {
41080                         this.doc.designMode="on";
41081                     } catch (e) {
41082                         return;
41083                     }
41084                     Roo.TaskMgr.stop(task);
41085                     this.initEditor.defer(10, this);
41086                 }
41087             },
41088             interval : 10,
41089             duration:10000,
41090             scope: this
41091         };
41092         Roo.TaskMgr.start(task);
41093
41094         if(!this.width){
41095             this.setSize(this.wrap.getSize());
41096         }
41097         if (this.resizeEl) {
41098             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
41099             // should trigger onReize..
41100         }
41101     },
41102
41103     // private
41104     onResize : function(w, h)
41105     {
41106         //Roo.log('resize: ' +w + ',' + h );
41107         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
41108         if(this.el && this.iframe){
41109             if(typeof w == 'number'){
41110                 var aw = w - this.wrap.getFrameWidth('lr');
41111                 this.el.setWidth(this.adjustWidth('textarea', aw));
41112                 this.iframe.style.width = aw + 'px';
41113             }
41114             if(typeof h == 'number'){
41115                 var tbh = 0;
41116                 for (var i =0; i < this.toolbars.length;i++) {
41117                     // fixme - ask toolbars for heights?
41118                     tbh += this.toolbars[i].tb.el.getHeight();
41119                     if (this.toolbars[i].footer) {
41120                         tbh += this.toolbars[i].footer.el.getHeight();
41121                     }
41122                 }
41123                 
41124                 
41125                 
41126                 
41127                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
41128                 ah -= 5; // knock a few pixes off for look..
41129                 this.el.setHeight(this.adjustWidth('textarea', ah));
41130                 this.iframe.style.height = ah + 'px';
41131                 if(this.doc){
41132                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
41133                 }
41134             }
41135         }
41136     },
41137
41138     /**
41139      * Toggles the editor between standard and source edit mode.
41140      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41141      */
41142     toggleSourceEdit : function(sourceEditMode){
41143         
41144         this.sourceEditMode = sourceEditMode === true;
41145         
41146         if(this.sourceEditMode){
41147 //            Roo.log('in');
41148 //            Roo.log(this.syncValue());
41149             this.syncValue();
41150             this.iframe.className = 'x-hidden';
41151             this.el.removeClass('x-hidden');
41152             this.el.dom.removeAttribute('tabIndex');
41153             this.el.focus();
41154         }else{
41155 //            Roo.log('out')
41156 //            Roo.log(this.pushValue()); 
41157             this.pushValue();
41158             this.iframe.className = '';
41159             this.el.addClass('x-hidden');
41160             this.el.dom.setAttribute('tabIndex', -1);
41161             this.deferFocus();
41162         }
41163         this.setSize(this.wrap.getSize());
41164         this.fireEvent('editmodechange', this, this.sourceEditMode);
41165     },
41166
41167     // private used internally
41168     createLink : function(){
41169         var url = prompt(this.createLinkText, this.defaultLinkValue);
41170         if(url && url != 'http:/'+'/'){
41171             this.relayCmd('createlink', url);
41172         }
41173     },
41174
41175     // private (for BoxComponent)
41176     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41177
41178     // private (for BoxComponent)
41179     getResizeEl : function(){
41180         return this.wrap;
41181     },
41182
41183     // private (for BoxComponent)
41184     getPositionEl : function(){
41185         return this.wrap;
41186     },
41187
41188     // private
41189     initEvents : function(){
41190         this.originalValue = this.getValue();
41191     },
41192
41193     /**
41194      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41195      * @method
41196      */
41197     markInvalid : Roo.emptyFn,
41198     /**
41199      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41200      * @method
41201      */
41202     clearInvalid : Roo.emptyFn,
41203
41204     setValue : function(v){
41205         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
41206         this.pushValue();
41207     },
41208
41209     /**
41210      * Protected method that will not generally be called directly. If you need/want
41211      * custom HTML cleanup, this is the method you should override.
41212      * @param {String} html The HTML to be cleaned
41213      * return {String} The cleaned HTML
41214      */
41215     cleanHtml : function(html){
41216         html = String(html);
41217         if(html.length > 5){
41218             if(Roo.isSafari){ // strip safari nonsense
41219                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41220             }
41221         }
41222         if(html == '&nbsp;'){
41223             html = '';
41224         }
41225         return html;
41226     },
41227
41228     /**
41229      * Protected method that will not generally be called directly. Syncs the contents
41230      * of the editor iframe with the textarea.
41231      */
41232     syncValue : function(){
41233         if(this.initialized){
41234             var bd = (this.doc.body || this.doc.documentElement);
41235             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41236             var html = bd.innerHTML;
41237             if(Roo.isSafari){
41238                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41239                 var m = bs.match(/text-align:(.*?);/i);
41240                 if(m && m[1]){
41241                     html = '<div style="'+m[0]+'">' + html + '</div>';
41242                 }
41243             }
41244             html = this.cleanHtml(html);
41245             // fix up the special chars.. normaly like back quotes in word...
41246             // however we do not want to do this with chinese..
41247             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41248                 var cc = b.charCodeAt();
41249                 if (
41250                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41251                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41252                     (cc >= 0xf900 && cc < 0xfb00 )
41253                 ) {
41254                         return b;
41255                 }
41256                 return "&#"+cc+";" 
41257             });
41258             if(this.fireEvent('beforesync', this, html) !== false){
41259                 this.el.dom.value = html;
41260                 this.fireEvent('sync', this, html);
41261             }
41262         }
41263     },
41264
41265     /**
41266      * Protected method that will not generally be called directly. Pushes the value of the textarea
41267      * into the iframe editor.
41268      */
41269     pushValue : function(){
41270         if(this.initialized){
41271             var v = this.el.dom.value;
41272             
41273             if(v.length < 1){
41274                 v = '&#160;';
41275             }
41276             
41277             if(this.fireEvent('beforepush', this, v) !== false){
41278                 var d = (this.doc.body || this.doc.documentElement);
41279                 d.innerHTML = v;
41280                 this.cleanUpPaste();
41281                 this.el.dom.value = d.innerHTML;
41282                 this.fireEvent('push', this, v);
41283             }
41284         }
41285     },
41286
41287     // private
41288     deferFocus : function(){
41289         this.focus.defer(10, this);
41290     },
41291
41292     // doc'ed in Field
41293     focus : function(){
41294         if(this.win && !this.sourceEditMode){
41295             this.win.focus();
41296         }else{
41297             this.el.focus();
41298         }
41299     },
41300     
41301     assignDocWin: function()
41302     {
41303         var iframe = this.iframe;
41304         
41305          if(Roo.isIE){
41306             this.doc = iframe.contentWindow.document;
41307             this.win = iframe.contentWindow;
41308         } else {
41309             if (!Roo.get(this.frameId)) {
41310                 return;
41311             }
41312             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41313             this.win = Roo.get(this.frameId).dom.contentWindow;
41314         }
41315     },
41316     
41317     // private
41318     initEditor : function(){
41319         //console.log("INIT EDITOR");
41320         this.assignDocWin();
41321         
41322         
41323         
41324         this.doc.designMode="on";
41325         this.doc.open();
41326         this.doc.write(this.getDocMarkup());
41327         this.doc.close();
41328         
41329         var dbody = (this.doc.body || this.doc.documentElement);
41330         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41331         // this copies styles from the containing element into thsi one..
41332         // not sure why we need all of this..
41333         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41334         ss['background-attachment'] = 'fixed'; // w3c
41335         dbody.bgProperties = 'fixed'; // ie
41336         Roo.DomHelper.applyStyles(dbody, ss);
41337         Roo.EventManager.on(this.doc, {
41338             //'mousedown': this.onEditorEvent,
41339             'mouseup': this.onEditorEvent,
41340             'dblclick': this.onEditorEvent,
41341             'click': this.onEditorEvent,
41342             'keyup': this.onEditorEvent,
41343             buffer:100,
41344             scope: this
41345         });
41346         if(Roo.isGecko){
41347             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41348         }
41349         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41350             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41351         }
41352         this.initialized = true;
41353
41354         this.fireEvent('initialize', this);
41355         this.pushValue();
41356     },
41357
41358     // private
41359     onDestroy : function(){
41360         
41361         
41362         
41363         if(this.rendered){
41364             
41365             for (var i =0; i < this.toolbars.length;i++) {
41366                 // fixme - ask toolbars for heights?
41367                 this.toolbars[i].onDestroy();
41368             }
41369             
41370             this.wrap.dom.innerHTML = '';
41371             this.wrap.remove();
41372         }
41373     },
41374
41375     // private
41376     onFirstFocus : function(){
41377         
41378         this.assignDocWin();
41379         
41380         
41381         this.activated = true;
41382         for (var i =0; i < this.toolbars.length;i++) {
41383             this.toolbars[i].onFirstFocus();
41384         }
41385        
41386         if(Roo.isGecko){ // prevent silly gecko errors
41387             this.win.focus();
41388             var s = this.win.getSelection();
41389             if(!s.focusNode || s.focusNode.nodeType != 3){
41390                 var r = s.getRangeAt(0);
41391                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41392                 r.collapse(true);
41393                 this.deferFocus();
41394             }
41395             try{
41396                 this.execCmd('useCSS', true);
41397                 this.execCmd('styleWithCSS', false);
41398             }catch(e){}
41399         }
41400         this.fireEvent('activate', this);
41401     },
41402
41403     // private
41404     adjustFont: function(btn){
41405         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41406         //if(Roo.isSafari){ // safari
41407         //    adjust *= 2;
41408        // }
41409         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41410         if(Roo.isSafari){ // safari
41411             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41412             v =  (v < 10) ? 10 : v;
41413             v =  (v > 48) ? 48 : v;
41414             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41415             
41416         }
41417         
41418         
41419         v = Math.max(1, v+adjust);
41420         
41421         this.execCmd('FontSize', v  );
41422     },
41423
41424     onEditorEvent : function(e){
41425         this.fireEvent('editorevent', this, e);
41426       //  this.updateToolbar();
41427         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41428     },
41429
41430     insertTag : function(tg)
41431     {
41432         // could be a bit smarter... -> wrap the current selected tRoo..
41433         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41434             
41435             range = this.createRange(this.getSelection());
41436             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41437             wrappingNode.appendChild(range.extractContents());
41438             range.insertNode(wrappingNode);
41439
41440             return;
41441             
41442             
41443             
41444         }
41445         this.execCmd("formatblock",   tg);
41446         
41447     },
41448     
41449     insertText : function(txt)
41450     {
41451         
41452         
41453         var range = this.createRange();
41454         range.deleteContents();
41455                //alert(Sender.getAttribute('label'));
41456                
41457         range.insertNode(this.doc.createTextNode(txt));
41458     } ,
41459     
41460     // private
41461     relayBtnCmd : function(btn){
41462         this.relayCmd(btn.cmd);
41463     },
41464
41465     /**
41466      * Executes a Midas editor command on the editor document and performs necessary focus and
41467      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41468      * @param {String} cmd The Midas command
41469      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41470      */
41471     relayCmd : function(cmd, value){
41472         this.win.focus();
41473         this.execCmd(cmd, value);
41474         this.fireEvent('editorevent', this);
41475         //this.updateToolbar();
41476         this.deferFocus();
41477     },
41478
41479     /**
41480      * Executes a Midas editor command directly on the editor document.
41481      * For visual commands, you should use {@link #relayCmd} instead.
41482      * <b>This should only be called after the editor is initialized.</b>
41483      * @param {String} cmd The Midas command
41484      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41485      */
41486     execCmd : function(cmd, value){
41487         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41488         this.syncValue();
41489     },
41490  
41491  
41492    
41493     /**
41494      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41495      * to insert tRoo.
41496      * @param {String} text | dom node.. 
41497      */
41498     insertAtCursor : function(text)
41499     {
41500         
41501         
41502         
41503         if(!this.activated){
41504             return;
41505         }
41506         /*
41507         if(Roo.isIE){
41508             this.win.focus();
41509             var r = this.doc.selection.createRange();
41510             if(r){
41511                 r.collapse(true);
41512                 r.pasteHTML(text);
41513                 this.syncValue();
41514                 this.deferFocus();
41515             
41516             }
41517             return;
41518         }
41519         */
41520         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41521             this.win.focus();
41522             
41523             
41524             // from jquery ui (MIT licenced)
41525             var range, node;
41526             var win = this.win;
41527             
41528             if (win.getSelection && win.getSelection().getRangeAt) {
41529                 range = win.getSelection().getRangeAt(0);
41530                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41531                 range.insertNode(node);
41532             } else if (win.document.selection && win.document.selection.createRange) {
41533                 // no firefox support
41534                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41535                 win.document.selection.createRange().pasteHTML(txt);
41536             } else {
41537                 // no firefox support
41538                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41539                 this.execCmd('InsertHTML', txt);
41540             } 
41541             
41542             this.syncValue();
41543             
41544             this.deferFocus();
41545         }
41546     },
41547  // private
41548     mozKeyPress : function(e){
41549         if(e.ctrlKey){
41550             var c = e.getCharCode(), cmd;
41551           
41552             if(c > 0){
41553                 c = String.fromCharCode(c).toLowerCase();
41554                 switch(c){
41555                     case 'b':
41556                         cmd = 'bold';
41557                         break;
41558                     case 'i':
41559                         cmd = 'italic';
41560                         break;
41561                     
41562                     case 'u':
41563                         cmd = 'underline';
41564                         break;
41565                     
41566                     case 'v':
41567                         this.cleanUpPaste.defer(100, this);
41568                         return;
41569                         
41570                 }
41571                 if(cmd){
41572                     this.win.focus();
41573                     this.execCmd(cmd);
41574                     this.deferFocus();
41575                     e.preventDefault();
41576                 }
41577                 
41578             }
41579         }
41580     },
41581
41582     // private
41583     fixKeys : function(){ // load time branching for fastest keydown performance
41584         if(Roo.isIE){
41585             return function(e){
41586                 var k = e.getKey(), r;
41587                 if(k == e.TAB){
41588                     e.stopEvent();
41589                     r = this.doc.selection.createRange();
41590                     if(r){
41591                         r.collapse(true);
41592                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41593                         this.deferFocus();
41594                     }
41595                     return;
41596                 }
41597                 
41598                 if(k == e.ENTER){
41599                     r = this.doc.selection.createRange();
41600                     if(r){
41601                         var target = r.parentElement();
41602                         if(!target || target.tagName.toLowerCase() != 'li'){
41603                             e.stopEvent();
41604                             r.pasteHTML('<br />');
41605                             r.collapse(false);
41606                             r.select();
41607                         }
41608                     }
41609                 }
41610                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41611                     this.cleanUpPaste.defer(100, this);
41612                     return;
41613                 }
41614                 
41615                 
41616             };
41617         }else if(Roo.isOpera){
41618             return function(e){
41619                 var k = e.getKey();
41620                 if(k == e.TAB){
41621                     e.stopEvent();
41622                     this.win.focus();
41623                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41624                     this.deferFocus();
41625                 }
41626                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41627                     this.cleanUpPaste.defer(100, this);
41628                     return;
41629                 }
41630                 
41631             };
41632         }else if(Roo.isSafari){
41633             return function(e){
41634                 var k = e.getKey();
41635                 
41636                 if(k == e.TAB){
41637                     e.stopEvent();
41638                     this.execCmd('InsertText','\t');
41639                     this.deferFocus();
41640                     return;
41641                 }
41642                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41643                     this.cleanUpPaste.defer(100, this);
41644                     return;
41645                 }
41646                 
41647              };
41648         }
41649     }(),
41650     
41651     getAllAncestors: function()
41652     {
41653         var p = this.getSelectedNode();
41654         var a = [];
41655         if (!p) {
41656             a.push(p); // push blank onto stack..
41657             p = this.getParentElement();
41658         }
41659         
41660         
41661         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41662             a.push(p);
41663             p = p.parentNode;
41664         }
41665         a.push(this.doc.body);
41666         return a;
41667     },
41668     lastSel : false,
41669     lastSelNode : false,
41670     
41671     
41672     getSelection : function() 
41673     {
41674         this.assignDocWin();
41675         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41676     },
41677     
41678     getSelectedNode: function() 
41679     {
41680         // this may only work on Gecko!!!
41681         
41682         // should we cache this!!!!
41683         
41684         
41685         
41686          
41687         var range = this.createRange(this.getSelection()).cloneRange();
41688         
41689         if (Roo.isIE) {
41690             var parent = range.parentElement();
41691             while (true) {
41692                 var testRange = range.duplicate();
41693                 testRange.moveToElementText(parent);
41694                 if (testRange.inRange(range)) {
41695                     break;
41696                 }
41697                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41698                     break;
41699                 }
41700                 parent = parent.parentElement;
41701             }
41702             return parent;
41703         }
41704         
41705         // is ancestor a text element.
41706         var ac =  range.commonAncestorContainer;
41707         if (ac.nodeType == 3) {
41708             ac = ac.parentNode;
41709         }
41710         
41711         var ar = ac.childNodes;
41712          
41713         var nodes = [];
41714         var other_nodes = [];
41715         var has_other_nodes = false;
41716         for (var i=0;i<ar.length;i++) {
41717             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41718                 continue;
41719             }
41720             // fullly contained node.
41721             
41722             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41723                 nodes.push(ar[i]);
41724                 continue;
41725             }
41726             
41727             // probably selected..
41728             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41729                 other_nodes.push(ar[i]);
41730                 continue;
41731             }
41732             // outer..
41733             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41734                 continue;
41735             }
41736             
41737             
41738             has_other_nodes = true;
41739         }
41740         if (!nodes.length && other_nodes.length) {
41741             nodes= other_nodes;
41742         }
41743         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41744             return false;
41745         }
41746         
41747         return nodes[0];
41748     },
41749     createRange: function(sel)
41750     {
41751         // this has strange effects when using with 
41752         // top toolbar - not sure if it's a great idea.
41753         //this.editor.contentWindow.focus();
41754         if (typeof sel != "undefined") {
41755             try {
41756                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41757             } catch(e) {
41758                 return this.doc.createRange();
41759             }
41760         } else {
41761             return this.doc.createRange();
41762         }
41763     },
41764     getParentElement: function()
41765     {
41766         
41767         this.assignDocWin();
41768         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41769         
41770         var range = this.createRange(sel);
41771          
41772         try {
41773             var p = range.commonAncestorContainer;
41774             while (p.nodeType == 3) { // text node
41775                 p = p.parentNode;
41776             }
41777             return p;
41778         } catch (e) {
41779             return null;
41780         }
41781     
41782     },
41783     /***
41784      *
41785      * Range intersection.. the hard stuff...
41786      *  '-1' = before
41787      *  '0' = hits..
41788      *  '1' = after.
41789      *         [ -- selected range --- ]
41790      *   [fail]                        [fail]
41791      *
41792      *    basically..
41793      *      if end is before start or  hits it. fail.
41794      *      if start is after end or hits it fail.
41795      *
41796      *   if either hits (but other is outside. - then it's not 
41797      *   
41798      *    
41799      **/
41800     
41801     
41802     // @see http://www.thismuchiknow.co.uk/?p=64.
41803     rangeIntersectsNode : function(range, node)
41804     {
41805         var nodeRange = node.ownerDocument.createRange();
41806         try {
41807             nodeRange.selectNode(node);
41808         } catch (e) {
41809             nodeRange.selectNodeContents(node);
41810         }
41811     
41812         var rangeStartRange = range.cloneRange();
41813         rangeStartRange.collapse(true);
41814     
41815         var rangeEndRange = range.cloneRange();
41816         rangeEndRange.collapse(false);
41817     
41818         var nodeStartRange = nodeRange.cloneRange();
41819         nodeStartRange.collapse(true);
41820     
41821         var nodeEndRange = nodeRange.cloneRange();
41822         nodeEndRange.collapse(false);
41823     
41824         return rangeStartRange.compareBoundaryPoints(
41825                  Range.START_TO_START, nodeEndRange) == -1 &&
41826                rangeEndRange.compareBoundaryPoints(
41827                  Range.START_TO_START, nodeStartRange) == 1;
41828         
41829          
41830     },
41831     rangeCompareNode : function(range, node)
41832     {
41833         var nodeRange = node.ownerDocument.createRange();
41834         try {
41835             nodeRange.selectNode(node);
41836         } catch (e) {
41837             nodeRange.selectNodeContents(node);
41838         }
41839         
41840         
41841         range.collapse(true);
41842     
41843         nodeRange.collapse(true);
41844      
41845         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41846         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41847          
41848         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41849         
41850         var nodeIsBefore   =  ss == 1;
41851         var nodeIsAfter    = ee == -1;
41852         
41853         if (nodeIsBefore && nodeIsAfter)
41854             return 0; // outer
41855         if (!nodeIsBefore && nodeIsAfter)
41856             return 1; //right trailed.
41857         
41858         if (nodeIsBefore && !nodeIsAfter)
41859             return 2;  // left trailed.
41860         // fully contined.
41861         return 3;
41862     },
41863
41864     // private? - in a new class?
41865     cleanUpPaste :  function()
41866     {
41867         // cleans up the whole document..
41868          Roo.log('cleanuppaste');
41869         this.cleanUpChildren(this.doc.body);
41870         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41871         if (clean != this.doc.body.innerHTML) {
41872             this.doc.body.innerHTML = clean;
41873         }
41874         
41875     },
41876     
41877     cleanWordChars : function(input) {// change the chars to hex code
41878         var he = Roo.form.HtmlEditor;
41879         
41880         var output = input;
41881         Roo.each(he.swapCodes, function(sw) { 
41882             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41883             
41884             output = output.replace(swapper, sw[1]);
41885         });
41886         
41887         return output;
41888     },
41889     
41890     
41891     cleanUpChildren : function (n)
41892     {
41893         if (!n.childNodes.length) {
41894             return;
41895         }
41896         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41897            this.cleanUpChild(n.childNodes[i]);
41898         }
41899     },
41900     
41901     
41902         
41903     
41904     cleanUpChild : function (node)
41905     {
41906         var ed = this;
41907         //console.log(node);
41908         if (node.nodeName == "#text") {
41909             // clean up silly Windows -- stuff?
41910             return; 
41911         }
41912         if (node.nodeName == "#comment") {
41913             node.parentNode.removeChild(node);
41914             // clean up silly Windows -- stuff?
41915             return; 
41916         }
41917         
41918         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41919             // remove node.
41920             node.parentNode.removeChild(node);
41921             return;
41922             
41923         }
41924         
41925         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41926         
41927         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41928         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41929         
41930         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41931         //    remove_keep_children = true;
41932         //}
41933         
41934         if (remove_keep_children) {
41935             this.cleanUpChildren(node);
41936             // inserts everything just before this node...
41937             while (node.childNodes.length) {
41938                 var cn = node.childNodes[0];
41939                 node.removeChild(cn);
41940                 node.parentNode.insertBefore(cn, node);
41941             }
41942             node.parentNode.removeChild(node);
41943             return;
41944         }
41945         
41946         if (!node.attributes || !node.attributes.length) {
41947             this.cleanUpChildren(node);
41948             return;
41949         }
41950         
41951         function cleanAttr(n,v)
41952         {
41953             
41954             if (v.match(/^\./) || v.match(/^\//)) {
41955                 return;
41956             }
41957             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41958                 return;
41959             }
41960             if (v.match(/^#/)) {
41961                 return;
41962             }
41963 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41964             node.removeAttribute(n);
41965             
41966         }
41967         
41968         function cleanStyle(n,v)
41969         {
41970             if (v.match(/expression/)) { //XSS?? should we even bother..
41971                 node.removeAttribute(n);
41972                 return;
41973             }
41974             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41975             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41976             
41977             
41978             var parts = v.split(/;/);
41979             var clean = [];
41980             
41981             Roo.each(parts, function(p) {
41982                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41983                 if (!p.length) {
41984                     return true;
41985                 }
41986                 var l = p.split(':').shift().replace(/\s+/g,'');
41987                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41988                 
41989                 
41990                 if ( cblack.indexOf(l) > -1) {
41991 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41992                     //node.removeAttribute(n);
41993                     return true;
41994                 }
41995                 //Roo.log()
41996                 // only allow 'c whitelisted system attributes'
41997                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41998 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41999                     //node.removeAttribute(n);
42000                     return true;
42001                 }
42002                 
42003                 
42004                  
42005                 
42006                 clean.push(p);
42007                 return true;
42008             });
42009             if (clean.length) { 
42010                 node.setAttribute(n, clean.join(';'));
42011             } else {
42012                 node.removeAttribute(n);
42013             }
42014             
42015         }
42016         
42017         
42018         for (var i = node.attributes.length-1; i > -1 ; i--) {
42019             var a = node.attributes[i];
42020             //console.log(a);
42021             
42022             if (a.name.toLowerCase().substr(0,2)=='on')  {
42023                 node.removeAttribute(a.name);
42024                 continue;
42025             }
42026             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
42027                 node.removeAttribute(a.name);
42028                 continue;
42029             }
42030             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
42031                 cleanAttr(a.name,a.value); // fixme..
42032                 continue;
42033             }
42034             if (a.name == 'style') {
42035                 cleanStyle(a.name,a.value);
42036                 continue;
42037             }
42038             /// clean up MS crap..
42039             // tecnically this should be a list of valid class'es..
42040             
42041             
42042             if (a.name == 'class') {
42043                 if (a.value.match(/^Mso/)) {
42044                     node.className = '';
42045                 }
42046                 
42047                 if (a.value.match(/body/)) {
42048                     node.className = '';
42049                 }
42050                 continue;
42051             }
42052             
42053             // style cleanup!?
42054             // class cleanup?
42055             
42056         }
42057         
42058         
42059         this.cleanUpChildren(node);
42060         
42061         
42062     }
42063     
42064     
42065     // hide stuff that is not compatible
42066     /**
42067      * @event blur
42068      * @hide
42069      */
42070     /**
42071      * @event change
42072      * @hide
42073      */
42074     /**
42075      * @event focus
42076      * @hide
42077      */
42078     /**
42079      * @event specialkey
42080      * @hide
42081      */
42082     /**
42083      * @cfg {String} fieldClass @hide
42084      */
42085     /**
42086      * @cfg {String} focusClass @hide
42087      */
42088     /**
42089      * @cfg {String} autoCreate @hide
42090      */
42091     /**
42092      * @cfg {String} inputType @hide
42093      */
42094     /**
42095      * @cfg {String} invalidClass @hide
42096      */
42097     /**
42098      * @cfg {String} invalidText @hide
42099      */
42100     /**
42101      * @cfg {String} msgFx @hide
42102      */
42103     /**
42104      * @cfg {String} validateOnBlur @hide
42105      */
42106 });
42107
42108 Roo.form.HtmlEditor.white = [
42109         'area', 'br', 'img', 'input', 'hr', 'wbr',
42110         
42111        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42112        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42113        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42114        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42115        'table',   'ul',         'xmp', 
42116        
42117        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42118       'thead',   'tr', 
42119      
42120       'dir', 'menu', 'ol', 'ul', 'dl',
42121        
42122       'embed',  'object'
42123 ];
42124
42125
42126 Roo.form.HtmlEditor.black = [
42127     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42128         'applet', // 
42129         'base',   'basefont', 'bgsound', 'blink',  'body', 
42130         'frame',  'frameset', 'head',    'html',   'ilayer', 
42131         'iframe', 'layer',  'link',     'meta',    'object',   
42132         'script', 'style' ,'title',  'xml' // clean later..
42133 ];
42134 Roo.form.HtmlEditor.clean = [
42135     'script', 'style', 'title', 'xml'
42136 ];
42137 Roo.form.HtmlEditor.remove = [
42138     'font'
42139 ];
42140 // attributes..
42141
42142 Roo.form.HtmlEditor.ablack = [
42143     'on'
42144 ];
42145     
42146 Roo.form.HtmlEditor.aclean = [ 
42147     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42148 ];
42149
42150 // protocols..
42151 Roo.form.HtmlEditor.pwhite= [
42152         'http',  'https',  'mailto'
42153 ];
42154
42155 // white listed style attributes.
42156 Roo.form.HtmlEditor.cwhite= [
42157       //  'text-align', /// default is to allow most things..
42158       
42159          
42160 //        'font-size'//??
42161 ];
42162
42163 // black listed style attributes.
42164 Roo.form.HtmlEditor.cblack= [
42165       //  'font-size' -- this can be set by the project 
42166 ];
42167
42168
42169 Roo.form.HtmlEditor.swapCodes   =[ 
42170     [    8211, "--" ], 
42171     [    8212, "--" ], 
42172     [    8216,  "'" ],  
42173     [    8217, "'" ],  
42174     [    8220, '"' ],  
42175     [    8221, '"' ],  
42176     [    8226, "*" ],  
42177     [    8230, "..." ]
42178 ]; 
42179
42180     // <script type="text/javascript">
42181 /*
42182  * Based on
42183  * Ext JS Library 1.1.1
42184  * Copyright(c) 2006-2007, Ext JS, LLC.
42185  *  
42186  
42187  */
42188
42189 /**
42190  * @class Roo.form.HtmlEditorToolbar1
42191  * Basic Toolbar
42192  * 
42193  * Usage:
42194  *
42195  new Roo.form.HtmlEditor({
42196     ....
42197     toolbars : [
42198         new Roo.form.HtmlEditorToolbar1({
42199             disable : { fonts: 1 , format: 1, ..., ... , ...],
42200             btns : [ .... ]
42201         })
42202     }
42203      
42204  * 
42205  * @cfg {Object} disable List of elements to disable..
42206  * @cfg {Array} btns List of additional buttons.
42207  * 
42208  * 
42209  * NEEDS Extra CSS? 
42210  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42211  */
42212  
42213 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42214 {
42215     
42216     Roo.apply(this, config);
42217     
42218     // default disabled, based on 'good practice'..
42219     this.disable = this.disable || {};
42220     Roo.applyIf(this.disable, {
42221         fontSize : true,
42222         colors : true,
42223         specialElements : true
42224     });
42225     
42226     
42227     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42228     // dont call parent... till later.
42229 }
42230
42231 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42232     
42233     tb: false,
42234     
42235     rendered: false,
42236     
42237     editor : false,
42238     /**
42239      * @cfg {Object} disable  List of toolbar elements to disable
42240          
42241      */
42242     disable : false,
42243       /**
42244      * @cfg {Array} fontFamilies An array of available font families
42245      */
42246     fontFamilies : [
42247         'Arial',
42248         'Courier New',
42249         'Tahoma',
42250         'Times New Roman',
42251         'Verdana'
42252     ],
42253     
42254     specialChars : [
42255            "&#169;",
42256           "&#174;",     
42257           "&#8482;",    
42258           "&#163;" ,    
42259          // "&#8212;",    
42260           "&#8230;",    
42261           "&#247;" ,    
42262         //  "&#225;" ,     ?? a acute?
42263            "&#8364;"    , //Euro
42264        //   "&#8220;"    ,
42265         //  "&#8221;"    ,
42266         //  "&#8226;"    ,
42267           "&#176;"  //   , // degrees
42268
42269          // "&#233;"     , // e ecute
42270          // "&#250;"     , // u ecute?
42271     ],
42272     
42273     specialElements : [
42274         {
42275             text: "Insert Table",
42276             xtype: 'MenuItem',
42277             xns : Roo.Menu,
42278             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42279                 
42280         },
42281         {    
42282             text: "Insert Image",
42283             xtype: 'MenuItem',
42284             xns : Roo.Menu,
42285             ihtml : '<img src="about:blank"/>'
42286             
42287         }
42288         
42289          
42290     ],
42291     
42292     
42293     inputElements : [ 
42294             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42295             "input:submit", "input:button", "select", "textarea", "label" ],
42296     formats : [
42297         ["p"] ,  
42298         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42299         ["pre"],[ "code"], 
42300         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42301         ['div'],['span']
42302     ],
42303     
42304     cleanStyles : [
42305         "font-size"
42306     ],
42307      /**
42308      * @cfg {String} defaultFont default font to use.
42309      */
42310     defaultFont: 'tahoma',
42311    
42312     fontSelect : false,
42313     
42314     
42315     formatCombo : false,
42316     
42317     init : function(editor)
42318     {
42319         this.editor = editor;
42320         
42321         
42322         var fid = editor.frameId;
42323         var etb = this;
42324         function btn(id, toggle, handler){
42325             var xid = fid + '-'+ id ;
42326             return {
42327                 id : xid,
42328                 cmd : id,
42329                 cls : 'x-btn-icon x-edit-'+id,
42330                 enableToggle:toggle !== false,
42331                 scope: editor, // was editor...
42332                 handler:handler||editor.relayBtnCmd,
42333                 clickEvent:'mousedown',
42334                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42335                 tabIndex:-1
42336             };
42337         }
42338         
42339         
42340         
42341         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42342         this.tb = tb;
42343          // stop form submits
42344         tb.el.on('click', function(e){
42345             e.preventDefault(); // what does this do?
42346         });
42347
42348         if(!this.disable.font) { // && !Roo.isSafari){
42349             /* why no safari for fonts 
42350             editor.fontSelect = tb.el.createChild({
42351                 tag:'select',
42352                 tabIndex: -1,
42353                 cls:'x-font-select',
42354                 html: this.createFontOptions()
42355             });
42356             
42357             editor.fontSelect.on('change', function(){
42358                 var font = editor.fontSelect.dom.value;
42359                 editor.relayCmd('fontname', font);
42360                 editor.deferFocus();
42361             }, editor);
42362             
42363             tb.add(
42364                 editor.fontSelect.dom,
42365                 '-'
42366             );
42367             */
42368             
42369         };
42370         if(!this.disable.formats){
42371             this.formatCombo = new Roo.form.ComboBox({
42372                 store: new Roo.data.SimpleStore({
42373                     id : 'tag',
42374                     fields: ['tag'],
42375                     data : this.formats // from states.js
42376                 }),
42377                 blockFocus : true,
42378                 name : '',
42379                 //autoCreate : {tag: "div",  size: "20"},
42380                 displayField:'tag',
42381                 typeAhead: false,
42382                 mode: 'local',
42383                 editable : false,
42384                 triggerAction: 'all',
42385                 emptyText:'Add tag',
42386                 selectOnFocus:true,
42387                 width:135,
42388                 listeners : {
42389                     'select': function(c, r, i) {
42390                         editor.insertTag(r.get('tag'));
42391                         editor.focus();
42392                     }
42393                 }
42394
42395             });
42396             tb.addField(this.formatCombo);
42397             
42398         }
42399         
42400         if(!this.disable.format){
42401             tb.add(
42402                 btn('bold'),
42403                 btn('italic'),
42404                 btn('underline')
42405             );
42406         };
42407         if(!this.disable.fontSize){
42408             tb.add(
42409                 '-',
42410                 
42411                 
42412                 btn('increasefontsize', false, editor.adjustFont),
42413                 btn('decreasefontsize', false, editor.adjustFont)
42414             );
42415         };
42416         
42417         
42418         if(!this.disable.colors){
42419             tb.add(
42420                 '-', {
42421                     id:editor.frameId +'-forecolor',
42422                     cls:'x-btn-icon x-edit-forecolor',
42423                     clickEvent:'mousedown',
42424                     tooltip: this.buttonTips['forecolor'] || undefined,
42425                     tabIndex:-1,
42426                     menu : new Roo.menu.ColorMenu({
42427                         allowReselect: true,
42428                         focus: Roo.emptyFn,
42429                         value:'000000',
42430                         plain:true,
42431                         selectHandler: function(cp, color){
42432                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42433                             editor.deferFocus();
42434                         },
42435                         scope: editor,
42436                         clickEvent:'mousedown'
42437                     })
42438                 }, {
42439                     id:editor.frameId +'backcolor',
42440                     cls:'x-btn-icon x-edit-backcolor',
42441                     clickEvent:'mousedown',
42442                     tooltip: this.buttonTips['backcolor'] || undefined,
42443                     tabIndex:-1,
42444                     menu : new Roo.menu.ColorMenu({
42445                         focus: Roo.emptyFn,
42446                         value:'FFFFFF',
42447                         plain:true,
42448                         allowReselect: true,
42449                         selectHandler: function(cp, color){
42450                             if(Roo.isGecko){
42451                                 editor.execCmd('useCSS', false);
42452                                 editor.execCmd('hilitecolor', color);
42453                                 editor.execCmd('useCSS', true);
42454                                 editor.deferFocus();
42455                             }else{
42456                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42457                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42458                                 editor.deferFocus();
42459                             }
42460                         },
42461                         scope:editor,
42462                         clickEvent:'mousedown'
42463                     })
42464                 }
42465             );
42466         };
42467         // now add all the items...
42468         
42469
42470         if(!this.disable.alignments){
42471             tb.add(
42472                 '-',
42473                 btn('justifyleft'),
42474                 btn('justifycenter'),
42475                 btn('justifyright')
42476             );
42477         };
42478
42479         //if(!Roo.isSafari){
42480             if(!this.disable.links){
42481                 tb.add(
42482                     '-',
42483                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
42484                 );
42485             };
42486
42487             if(!this.disable.lists){
42488                 tb.add(
42489                     '-',
42490                     btn('insertorderedlist'),
42491                     btn('insertunorderedlist')
42492                 );
42493             }
42494             if(!this.disable.sourceEdit){
42495                 tb.add(
42496                     '-',
42497                     btn('sourceedit', true, function(btn){
42498                         this.toggleSourceEdit(btn.pressed);
42499                     })
42500                 );
42501             }
42502         //}
42503         
42504         var smenu = { };
42505         // special menu.. - needs to be tidied up..
42506         if (!this.disable.special) {
42507             smenu = {
42508                 text: "&#169;",
42509                 cls: 'x-edit-none',
42510                 
42511                 menu : {
42512                     items : []
42513                 }
42514             };
42515             for (var i =0; i < this.specialChars.length; i++) {
42516                 smenu.menu.items.push({
42517                     
42518                     html: this.specialChars[i],
42519                     handler: function(a,b) {
42520                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42521                         //editor.insertAtCursor(a.html);
42522                         
42523                     },
42524                     tabIndex:-1
42525                 });
42526             }
42527             
42528             
42529             tb.add(smenu);
42530             
42531             
42532         }
42533         
42534         var cmenu = { };
42535         if (!this.disable.cleanStyles) {
42536             cmenu = {
42537                 cls: 'x-btn-icon x-btn-clear',
42538                 
42539                 menu : {
42540                     items : []
42541                 }
42542             };
42543             for (var i =0; i < this.cleanStyles.length; i++) {
42544                 cmenu.menu.items.push({
42545                     actiontype : this.cleanStyles[i],
42546                     html: 'Remove ' + this.cleanStyles[i],
42547                     handler: function(a,b) {
42548                         Roo.log(a);
42549                         Roo.log(b);
42550                         var c = Roo.get(editor.doc.body);
42551                         c.select('[style]').each(function(s) {
42552                             s.dom.style.removeProperty(a.actiontype);
42553                         });
42554                         
42555                     },
42556                     tabIndex:-1
42557                 });
42558             }
42559             
42560             tb.add(cmenu);
42561         }
42562          
42563         if (!this.disable.specialElements) {
42564             var semenu = {
42565                 text: "Other;",
42566                 cls: 'x-edit-none',
42567                 menu : {
42568                     items : []
42569                 }
42570             };
42571             for (var i =0; i < this.specialElements.length; i++) {
42572                 semenu.menu.items.push(
42573                     Roo.apply({ 
42574                         handler: function(a,b) {
42575                             editor.insertAtCursor(this.ihtml);
42576                         }
42577                     }, this.specialElements[i])
42578                 );
42579                     
42580             }
42581             
42582             tb.add(semenu);
42583             
42584             
42585         }
42586          
42587         
42588         if (this.btns) {
42589             for(var i =0; i< this.btns.length;i++) {
42590                 var b = Roo.factory(this.btns[i],Roo.form);
42591                 b.cls =  'x-edit-none';
42592                 b.scope = editor;
42593                 tb.add(b);
42594             }
42595         
42596         }
42597         
42598         
42599         
42600         // disable everything...
42601         
42602         this.tb.items.each(function(item){
42603            if(item.id != editor.frameId+ '-sourceedit'){
42604                 item.disable();
42605             }
42606         });
42607         this.rendered = true;
42608         
42609         // the all the btns;
42610         editor.on('editorevent', this.updateToolbar, this);
42611         // other toolbars need to implement this..
42612         //editor.on('editmodechange', this.updateToolbar, this);
42613     },
42614     
42615     
42616     
42617     /**
42618      * Protected method that will not generally be called directly. It triggers
42619      * a toolbar update by reading the markup state of the current selection in the editor.
42620      */
42621     updateToolbar: function(){
42622
42623         if(!this.editor.activated){
42624             this.editor.onFirstFocus();
42625             return;
42626         }
42627
42628         var btns = this.tb.items.map, 
42629             doc = this.editor.doc,
42630             frameId = this.editor.frameId;
42631
42632         if(!this.disable.font && !Roo.isSafari){
42633             /*
42634             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42635             if(name != this.fontSelect.dom.value){
42636                 this.fontSelect.dom.value = name;
42637             }
42638             */
42639         }
42640         if(!this.disable.format){
42641             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42642             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42643             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42644         }
42645         if(!this.disable.alignments){
42646             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42647             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42648             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42649         }
42650         if(!Roo.isSafari && !this.disable.lists){
42651             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42652             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42653         }
42654         
42655         var ans = this.editor.getAllAncestors();
42656         if (this.formatCombo) {
42657             
42658             
42659             var store = this.formatCombo.store;
42660             this.formatCombo.setValue("");
42661             for (var i =0; i < ans.length;i++) {
42662                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42663                     // select it..
42664                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42665                     break;
42666                 }
42667             }
42668         }
42669         
42670         
42671         
42672         // hides menus... - so this cant be on a menu...
42673         Roo.menu.MenuMgr.hideAll();
42674
42675         //this.editorsyncValue();
42676     },
42677    
42678     
42679     createFontOptions : function(){
42680         var buf = [], fs = this.fontFamilies, ff, lc;
42681         
42682         
42683         
42684         for(var i = 0, len = fs.length; i< len; i++){
42685             ff = fs[i];
42686             lc = ff.toLowerCase();
42687             buf.push(
42688                 '<option value="',lc,'" style="font-family:',ff,';"',
42689                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42690                     ff,
42691                 '</option>'
42692             );
42693         }
42694         return buf.join('');
42695     },
42696     
42697     toggleSourceEdit : function(sourceEditMode){
42698         if(sourceEditMode === undefined){
42699             sourceEditMode = !this.sourceEditMode;
42700         }
42701         this.sourceEditMode = sourceEditMode === true;
42702         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42703         // just toggle the button?
42704         if(btn.pressed !== this.editor.sourceEditMode){
42705             btn.toggle(this.editor.sourceEditMode);
42706             return;
42707         }
42708         
42709         if(this.sourceEditMode){
42710             this.tb.items.each(function(item){
42711                 if(item.cmd != 'sourceedit'){
42712                     item.disable();
42713                 }
42714             });
42715           
42716         }else{
42717             if(this.initialized){
42718                 this.tb.items.each(function(item){
42719                     item.enable();
42720                 });
42721             }
42722             
42723         }
42724         // tell the editor that it's been pressed..
42725         this.editor.toggleSourceEdit(sourceEditMode);
42726        
42727     },
42728      /**
42729      * Object collection of toolbar tooltips for the buttons in the editor. The key
42730      * is the command id associated with that button and the value is a valid QuickTips object.
42731      * For example:
42732 <pre><code>
42733 {
42734     bold : {
42735         title: 'Bold (Ctrl+B)',
42736         text: 'Make the selected text bold.',
42737         cls: 'x-html-editor-tip'
42738     },
42739     italic : {
42740         title: 'Italic (Ctrl+I)',
42741         text: 'Make the selected text italic.',
42742         cls: 'x-html-editor-tip'
42743     },
42744     ...
42745 </code></pre>
42746     * @type Object
42747      */
42748     buttonTips : {
42749         bold : {
42750             title: 'Bold (Ctrl+B)',
42751             text: 'Make the selected text bold.',
42752             cls: 'x-html-editor-tip'
42753         },
42754         italic : {
42755             title: 'Italic (Ctrl+I)',
42756             text: 'Make the selected text italic.',
42757             cls: 'x-html-editor-tip'
42758         },
42759         underline : {
42760             title: 'Underline (Ctrl+U)',
42761             text: 'Underline the selected text.',
42762             cls: 'x-html-editor-tip'
42763         },
42764         increasefontsize : {
42765             title: 'Grow Text',
42766             text: 'Increase the font size.',
42767             cls: 'x-html-editor-tip'
42768         },
42769         decreasefontsize : {
42770             title: 'Shrink Text',
42771             text: 'Decrease the font size.',
42772             cls: 'x-html-editor-tip'
42773         },
42774         backcolor : {
42775             title: 'Text Highlight Color',
42776             text: 'Change the background color of the selected text.',
42777             cls: 'x-html-editor-tip'
42778         },
42779         forecolor : {
42780             title: 'Font Color',
42781             text: 'Change the color of the selected text.',
42782             cls: 'x-html-editor-tip'
42783         },
42784         justifyleft : {
42785             title: 'Align Text Left',
42786             text: 'Align text to the left.',
42787             cls: 'x-html-editor-tip'
42788         },
42789         justifycenter : {
42790             title: 'Center Text',
42791             text: 'Center text in the editor.',
42792             cls: 'x-html-editor-tip'
42793         },
42794         justifyright : {
42795             title: 'Align Text Right',
42796             text: 'Align text to the right.',
42797             cls: 'x-html-editor-tip'
42798         },
42799         insertunorderedlist : {
42800             title: 'Bullet List',
42801             text: 'Start a bulleted list.',
42802             cls: 'x-html-editor-tip'
42803         },
42804         insertorderedlist : {
42805             title: 'Numbered List',
42806             text: 'Start a numbered list.',
42807             cls: 'x-html-editor-tip'
42808         },
42809         createlink : {
42810             title: 'Hyperlink',
42811             text: 'Make the selected text a hyperlink.',
42812             cls: 'x-html-editor-tip'
42813         },
42814         sourceedit : {
42815             title: 'Source Edit',
42816             text: 'Switch to source editing mode.',
42817             cls: 'x-html-editor-tip'
42818         }
42819     },
42820     // private
42821     onDestroy : function(){
42822         if(this.rendered){
42823             
42824             this.tb.items.each(function(item){
42825                 if(item.menu){
42826                     item.menu.removeAll();
42827                     if(item.menu.el){
42828                         item.menu.el.destroy();
42829                     }
42830                 }
42831                 item.destroy();
42832             });
42833              
42834         }
42835     },
42836     onFirstFocus: function() {
42837         this.tb.items.each(function(item){
42838            item.enable();
42839         });
42840     }
42841 });
42842
42843
42844
42845
42846 // <script type="text/javascript">
42847 /*
42848  * Based on
42849  * Ext JS Library 1.1.1
42850  * Copyright(c) 2006-2007, Ext JS, LLC.
42851  *  
42852  
42853  */
42854
42855  
42856 /**
42857  * @class Roo.form.HtmlEditor.ToolbarContext
42858  * Context Toolbar
42859  * 
42860  * Usage:
42861  *
42862  new Roo.form.HtmlEditor({
42863     ....
42864     toolbars : [
42865         { xtype: 'ToolbarStandard', styles : {} }
42866         { xtype: 'ToolbarContext', disable : {} }
42867     ]
42868 })
42869
42870      
42871  * 
42872  * @config : {Object} disable List of elements to disable.. (not done yet.)
42873  * @config : {Object} styles  Map of styles available.
42874  * 
42875  */
42876
42877 Roo.form.HtmlEditor.ToolbarContext = function(config)
42878 {
42879     
42880     Roo.apply(this, config);
42881     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42882     // dont call parent... till later.
42883     this.styles = this.styles || {};
42884 }
42885
42886  
42887
42888 Roo.form.HtmlEditor.ToolbarContext.types = {
42889     'IMG' : {
42890         width : {
42891             title: "Width",
42892             width: 40
42893         },
42894         height:  {
42895             title: "Height",
42896             width: 40
42897         },
42898         align: {
42899             title: "Align",
42900             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42901             width : 80
42902             
42903         },
42904         border: {
42905             title: "Border",
42906             width: 40
42907         },
42908         alt: {
42909             title: "Alt",
42910             width: 120
42911         },
42912         src : {
42913             title: "Src",
42914             width: 220
42915         }
42916         
42917     },
42918     'A' : {
42919         name : {
42920             title: "Name",
42921             width: 50
42922         },
42923         target:  {
42924             title: "Target",
42925             width: 120
42926         },
42927         href:  {
42928             title: "Href",
42929             width: 220
42930         } // border?
42931         
42932     },
42933     'TABLE' : {
42934         rows : {
42935             title: "Rows",
42936             width: 20
42937         },
42938         cols : {
42939             title: "Cols",
42940             width: 20
42941         },
42942         width : {
42943             title: "Width",
42944             width: 40
42945         },
42946         height : {
42947             title: "Height",
42948             width: 40
42949         },
42950         border : {
42951             title: "Border",
42952             width: 20
42953         }
42954     },
42955     'TD' : {
42956         width : {
42957             title: "Width",
42958             width: 40
42959         },
42960         height : {
42961             title: "Height",
42962             width: 40
42963         },   
42964         align: {
42965             title: "Align",
42966             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42967             width: 80
42968         },
42969         valign: {
42970             title: "Valign",
42971             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42972             width: 80
42973         },
42974         colspan: {
42975             title: "Colspan",
42976             width: 20
42977             
42978         },
42979          'font-family'  : {
42980             title : "Font",
42981             style : 'fontFamily',
42982             displayField: 'display',
42983             optname : 'font-family',
42984             width: 140
42985         }
42986     },
42987     'INPUT' : {
42988         name : {
42989             title: "name",
42990             width: 120
42991         },
42992         value : {
42993             title: "Value",
42994             width: 120
42995         },
42996         width : {
42997             title: "Width",
42998             width: 40
42999         }
43000     },
43001     'LABEL' : {
43002         'for' : {
43003             title: "For",
43004             width: 120
43005         }
43006     },
43007     'TEXTAREA' : {
43008           name : {
43009             title: "name",
43010             width: 120
43011         },
43012         rows : {
43013             title: "Rows",
43014             width: 20
43015         },
43016         cols : {
43017             title: "Cols",
43018             width: 20
43019         }
43020     },
43021     'SELECT' : {
43022         name : {
43023             title: "name",
43024             width: 120
43025         },
43026         selectoptions : {
43027             title: "Options",
43028             width: 200
43029         }
43030     },
43031     
43032     // should we really allow this??
43033     // should this just be 
43034     'BODY' : {
43035         title : {
43036             title: "Title",
43037             width: 200,
43038             disabled : true
43039         }
43040     },
43041     'SPAN' : {
43042         'font-family'  : {
43043             title : "Font",
43044             style : 'fontFamily',
43045             displayField: 'display',
43046             optname : 'font-family',
43047             width: 140
43048         }
43049     },
43050     'DIV' : {
43051         'font-family'  : {
43052             title : "Font",
43053             style : 'fontFamily',
43054             displayField: 'display',
43055             optname : 'font-family',
43056             width: 140
43057         }
43058     },
43059      'P' : {
43060         'font-family'  : {
43061             title : "Font",
43062             style : 'fontFamily',
43063             displayField: 'display',
43064             optname : 'font-family',
43065             width: 140
43066         }
43067     },
43068     
43069     '*' : {
43070         // empty..
43071     }
43072
43073 };
43074
43075 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43076 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43077
43078 Roo.form.HtmlEditor.ToolbarContext.options = {
43079         'font-family'  : [ 
43080                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43081                 [ 'Courier New', 'Courier New'],
43082                 [ 'Tahoma', 'Tahoma'],
43083                 [ 'Times New Roman,serif', 'Times'],
43084                 [ 'Verdana','Verdana' ]
43085         ]
43086 };
43087
43088 // fixme - these need to be configurable..
43089  
43090
43091 Roo.form.HtmlEditor.ToolbarContext.types
43092
43093
43094 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43095     
43096     tb: false,
43097     
43098     rendered: false,
43099     
43100     editor : false,
43101     /**
43102      * @cfg {Object} disable  List of toolbar elements to disable
43103          
43104      */
43105     disable : false,
43106     /**
43107      * @cfg {Object} styles List of styles 
43108      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43109      *
43110      * These must be defined in the page, so they get rendered correctly..
43111      * .headline { }
43112      * TD.underline { }
43113      * 
43114      */
43115     styles : false,
43116     
43117     options: false,
43118     
43119     toolbars : false,
43120     
43121     init : function(editor)
43122     {
43123         this.editor = editor;
43124         
43125         
43126         var fid = editor.frameId;
43127         var etb = this;
43128         function btn(id, toggle, handler){
43129             var xid = fid + '-'+ id ;
43130             return {
43131                 id : xid,
43132                 cmd : id,
43133                 cls : 'x-btn-icon x-edit-'+id,
43134                 enableToggle:toggle !== false,
43135                 scope: editor, // was editor...
43136                 handler:handler||editor.relayBtnCmd,
43137                 clickEvent:'mousedown',
43138                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43139                 tabIndex:-1
43140             };
43141         }
43142         // create a new element.
43143         var wdiv = editor.wrap.createChild({
43144                 tag: 'div'
43145             }, editor.wrap.dom.firstChild.nextSibling, true);
43146         
43147         // can we do this more than once??
43148         
43149          // stop form submits
43150       
43151  
43152         // disable everything...
43153         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43154         this.toolbars = {};
43155            
43156         for (var i in  ty) {
43157           
43158             this.toolbars[i] = this.buildToolbar(ty[i],i);
43159         }
43160         this.tb = this.toolbars.BODY;
43161         this.tb.el.show();
43162         this.buildFooter();
43163         this.footer.show();
43164         editor.on('hide', function( ) { this.footer.hide() }, this);
43165         editor.on('show', function( ) { this.footer.show() }, this);
43166         
43167          
43168         this.rendered = true;
43169         
43170         // the all the btns;
43171         editor.on('editorevent', this.updateToolbar, this);
43172         // other toolbars need to implement this..
43173         //editor.on('editmodechange', this.updateToolbar, this);
43174     },
43175     
43176     
43177     
43178     /**
43179      * Protected method that will not generally be called directly. It triggers
43180      * a toolbar update by reading the markup state of the current selection in the editor.
43181      */
43182     updateToolbar: function(editor,ev,sel){
43183
43184         //Roo.log(ev);
43185         // capture mouse up - this is handy for selecting images..
43186         // perhaps should go somewhere else...
43187         if(!this.editor.activated){
43188              this.editor.onFirstFocus();
43189             return;
43190         }
43191         
43192         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43193         // selectNode - might want to handle IE?
43194         if (ev &&
43195             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43196             ev.target && ev.target.tagName == 'IMG') {
43197             // they have click on an image...
43198             // let's see if we can change the selection...
43199             sel = ev.target;
43200          
43201               var nodeRange = sel.ownerDocument.createRange();
43202             try {
43203                 nodeRange.selectNode(sel);
43204             } catch (e) {
43205                 nodeRange.selectNodeContents(sel);
43206             }
43207             //nodeRange.collapse(true);
43208             var s = editor.win.getSelection();
43209             s.removeAllRanges();
43210             s.addRange(nodeRange);
43211         }  
43212         
43213       
43214         var updateFooter = sel ? false : true;
43215         
43216         
43217         var ans = this.editor.getAllAncestors();
43218         
43219         // pick
43220         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43221         
43222         if (!sel) { 
43223             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
43224             sel = sel ? sel : this.editor.doc.body;
43225             sel = sel.tagName.length ? sel : this.editor.doc.body;
43226             
43227         }
43228         // pick a menu that exists..
43229         var tn = sel.tagName.toUpperCase();
43230         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43231         
43232         tn = sel.tagName.toUpperCase();
43233         
43234         var lastSel = this.tb.selectedNode
43235         
43236         this.tb.selectedNode = sel;
43237         
43238         // if current menu does not match..
43239         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43240                 
43241             this.tb.el.hide();
43242             ///console.log("show: " + tn);
43243             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43244             this.tb.el.show();
43245             // update name
43246             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43247             
43248             
43249             // update attributes
43250             if (this.tb.fields) {
43251                 this.tb.fields.each(function(e) {
43252                     if (e.stylename) {
43253                         e.setValue(sel.style[e.stylename]);
43254                         return;
43255                     } 
43256                    e.setValue(sel.getAttribute(e.attrname));
43257                 });
43258             }
43259             
43260             var hasStyles = false;
43261             for(var i in this.styles) {
43262                 hasStyles = true;
43263                 break;
43264             }
43265             
43266             // update styles
43267             if (hasStyles) { 
43268                 var st = this.tb.fields.item(0);
43269                 
43270                 st.store.removeAll();
43271                
43272                 
43273                 var cn = sel.className.split(/\s+/);
43274                 
43275                 var avs = [];
43276                 if (this.styles['*']) {
43277                     
43278                     Roo.each(this.styles['*'], function(v) {
43279                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43280                     });
43281                 }
43282                 if (this.styles[tn]) { 
43283                     Roo.each(this.styles[tn], function(v) {
43284                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43285                     });
43286                 }
43287                 
43288                 st.store.loadData(avs);
43289                 st.collapse();
43290                 st.setValue(cn);
43291             }
43292             // flag our selected Node.
43293             this.tb.selectedNode = sel;
43294            
43295            
43296             Roo.menu.MenuMgr.hideAll();
43297
43298         }
43299         
43300         if (!updateFooter) {
43301             //this.footDisp.dom.innerHTML = ''; 
43302             return;
43303         }
43304         // update the footer
43305         //
43306         var html = '';
43307         
43308         this.footerEls = ans.reverse();
43309         Roo.each(this.footerEls, function(a,i) {
43310             if (!a) { return; }
43311             html += html.length ? ' &gt; '  :  '';
43312             
43313             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43314             
43315         });
43316        
43317         // 
43318         var sz = this.footDisp.up('td').getSize();
43319         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43320         this.footDisp.dom.style.marginLeft = '5px';
43321         
43322         this.footDisp.dom.style.overflow = 'hidden';
43323         
43324         this.footDisp.dom.innerHTML = html;
43325             
43326         //this.editorsyncValue();
43327     },
43328      
43329     
43330    
43331        
43332     // private
43333     onDestroy : function(){
43334         if(this.rendered){
43335             
43336             this.tb.items.each(function(item){
43337                 if(item.menu){
43338                     item.menu.removeAll();
43339                     if(item.menu.el){
43340                         item.menu.el.destroy();
43341                     }
43342                 }
43343                 item.destroy();
43344             });
43345              
43346         }
43347     },
43348     onFirstFocus: function() {
43349         // need to do this for all the toolbars..
43350         this.tb.items.each(function(item){
43351            item.enable();
43352         });
43353     },
43354     buildToolbar: function(tlist, nm)
43355     {
43356         var editor = this.editor;
43357          // create a new element.
43358         var wdiv = editor.wrap.createChild({
43359                 tag: 'div'
43360             }, editor.wrap.dom.firstChild.nextSibling, true);
43361         
43362        
43363         var tb = new Roo.Toolbar(wdiv);
43364         // add the name..
43365         
43366         tb.add(nm+ ":&nbsp;");
43367         
43368         var styles = [];
43369         for(var i in this.styles) {
43370             styles.push(i);
43371         }
43372         
43373         // styles...
43374         if (styles && styles.length) {
43375             
43376             // this needs a multi-select checkbox...
43377             tb.addField( new Roo.form.ComboBox({
43378                 store: new Roo.data.SimpleStore({
43379                     id : 'val',
43380                     fields: ['val', 'selected'],
43381                     data : [] 
43382                 }),
43383                 name : '-roo-edit-className',
43384                 attrname : 'className',
43385                 displayField: 'val',
43386                 typeAhead: false,
43387                 mode: 'local',
43388                 editable : false,
43389                 triggerAction: 'all',
43390                 emptyText:'Select Style',
43391                 selectOnFocus:true,
43392                 width: 130,
43393                 listeners : {
43394                     'select': function(c, r, i) {
43395                         // initial support only for on class per el..
43396                         tb.selectedNode.className =  r ? r.get('val') : '';
43397                         editor.syncValue();
43398                     }
43399                 }
43400     
43401             }));
43402         }
43403         
43404         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43405         var tbops = tbc.options;
43406         
43407         for (var i in tlist) {
43408             
43409             var item = tlist[i];
43410             tb.add(item.title + ":&nbsp;");
43411             
43412             
43413             //optname == used so you can configure the options available..
43414             var opts = item.opts ? item.opts : false;
43415             if (item.optname) {
43416                 opts = tbops[item.optname];
43417            
43418             }
43419             
43420             if (opts) {
43421                 // opts == pulldown..
43422                 tb.addField( new Roo.form.ComboBox({
43423                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43424                         id : 'val',
43425                         fields: ['val', 'display'],
43426                         data : opts  
43427                     }),
43428                     name : '-roo-edit-' + i,
43429                     attrname : i,
43430                     stylename : item.style ? item.style : false,
43431                     displayField: item.displayField ? item.displayField : 'val',
43432                     valueField :  'val',
43433                     typeAhead: false,
43434                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43435                     editable : false,
43436                     triggerAction: 'all',
43437                     emptyText:'Select',
43438                     selectOnFocus:true,
43439                     width: item.width ? item.width  : 130,
43440                     listeners : {
43441                         'select': function(c, r, i) {
43442                             if (c.stylename) {
43443                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43444                                 return;
43445                             }
43446                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43447                         }
43448                     }
43449
43450                 }));
43451                 continue;
43452                     
43453                  
43454                 
43455                 tb.addField( new Roo.form.TextField({
43456                     name: i,
43457                     width: 100,
43458                     //allowBlank:false,
43459                     value: ''
43460                 }));
43461                 continue;
43462             }
43463             tb.addField( new Roo.form.TextField({
43464                 name: '-roo-edit-' + i,
43465                 attrname : i,
43466                 
43467                 width: item.width,
43468                 //allowBlank:true,
43469                 value: '',
43470                 listeners: {
43471                     'change' : function(f, nv, ov) {
43472                         tb.selectedNode.setAttribute(f.attrname, nv);
43473                     }
43474                 }
43475             }));
43476              
43477         }
43478         tb.addFill();
43479         var _this = this;
43480         tb.addButton( {
43481             text: 'Remove Tag',
43482     
43483             listeners : {
43484                 click : function ()
43485                 {
43486                     // remove
43487                     // undo does not work.
43488                      
43489                     var sn = tb.selectedNode;
43490                     
43491                     var pn = sn.parentNode;
43492                     
43493                     var stn =  sn.childNodes[0];
43494                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43495                     while (sn.childNodes.length) {
43496                         var node = sn.childNodes[0];
43497                         sn.removeChild(node);
43498                         //Roo.log(node);
43499                         pn.insertBefore(node, sn);
43500                         
43501                     }
43502                     pn.removeChild(sn);
43503                     var range = editor.createRange();
43504         
43505                     range.setStart(stn,0);
43506                     range.setEnd(en,0); //????
43507                     //range.selectNode(sel);
43508                     
43509                     
43510                     var selection = editor.getSelection();
43511                     selection.removeAllRanges();
43512                     selection.addRange(range);
43513                     
43514                     
43515                     
43516                     //_this.updateToolbar(null, null, pn);
43517                     _this.updateToolbar(null, null, null);
43518                     _this.footDisp.dom.innerHTML = ''; 
43519                 }
43520             }
43521             
43522                     
43523                 
43524             
43525         });
43526         
43527         
43528         tb.el.on('click', function(e){
43529             e.preventDefault(); // what does this do?
43530         });
43531         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43532         tb.el.hide();
43533         tb.name = nm;
43534         // dont need to disable them... as they will get hidden
43535         return tb;
43536          
43537         
43538     },
43539     buildFooter : function()
43540     {
43541         
43542         var fel = this.editor.wrap.createChild();
43543         this.footer = new Roo.Toolbar(fel);
43544         // toolbar has scrolly on left / right?
43545         var footDisp= new Roo.Toolbar.Fill();
43546         var _t = this;
43547         this.footer.add(
43548             {
43549                 text : '&lt;',
43550                 xtype: 'Button',
43551                 handler : function() {
43552                     _t.footDisp.scrollTo('left',0,true)
43553                 }
43554             }
43555         );
43556         this.footer.add( footDisp );
43557         this.footer.add( 
43558             {
43559                 text : '&gt;',
43560                 xtype: 'Button',
43561                 handler : function() {
43562                     // no animation..
43563                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43564                 }
43565             }
43566         );
43567         var fel = Roo.get(footDisp.el);
43568         fel.addClass('x-editor-context');
43569         this.footDispWrap = fel; 
43570         this.footDispWrap.overflow  = 'hidden';
43571         
43572         this.footDisp = fel.createChild();
43573         this.footDispWrap.on('click', this.onContextClick, this)
43574         
43575         
43576     },
43577     onContextClick : function (ev,dom)
43578     {
43579         ev.preventDefault();
43580         var  cn = dom.className;
43581         //Roo.log(cn);
43582         if (!cn.match(/x-ed-loc-/)) {
43583             return;
43584         }
43585         var n = cn.split('-').pop();
43586         var ans = this.footerEls;
43587         var sel = ans[n];
43588         
43589          // pick
43590         var range = this.editor.createRange();
43591         
43592         range.selectNodeContents(sel);
43593         //range.selectNode(sel);
43594         
43595         
43596         var selection = this.editor.getSelection();
43597         selection.removeAllRanges();
43598         selection.addRange(range);
43599         
43600         
43601         
43602         this.updateToolbar(null, null, sel);
43603         
43604         
43605     }
43606     
43607     
43608     
43609     
43610     
43611 });
43612
43613
43614
43615
43616
43617 /*
43618  * Based on:
43619  * Ext JS Library 1.1.1
43620  * Copyright(c) 2006-2007, Ext JS, LLC.
43621  *
43622  * Originally Released Under LGPL - original licence link has changed is not relivant.
43623  *
43624  * Fork - LGPL
43625  * <script type="text/javascript">
43626  */
43627  
43628 /**
43629  * @class Roo.form.BasicForm
43630  * @extends Roo.util.Observable
43631  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43632  * @constructor
43633  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43634  * @param {Object} config Configuration options
43635  */
43636 Roo.form.BasicForm = function(el, config){
43637     this.allItems = [];
43638     this.childForms = [];
43639     Roo.apply(this, config);
43640     /*
43641      * The Roo.form.Field items in this form.
43642      * @type MixedCollection
43643      */
43644      
43645      
43646     this.items = new Roo.util.MixedCollection(false, function(o){
43647         return o.id || (o.id = Roo.id());
43648     });
43649     this.addEvents({
43650         /**
43651          * @event beforeaction
43652          * Fires before any action is performed. Return false to cancel the action.
43653          * @param {Form} this
43654          * @param {Action} action The action to be performed
43655          */
43656         beforeaction: true,
43657         /**
43658          * @event actionfailed
43659          * Fires when an action fails.
43660          * @param {Form} this
43661          * @param {Action} action The action that failed
43662          */
43663         actionfailed : true,
43664         /**
43665          * @event actioncomplete
43666          * Fires when an action is completed.
43667          * @param {Form} this
43668          * @param {Action} action The action that completed
43669          */
43670         actioncomplete : true
43671     });
43672     if(el){
43673         this.initEl(el);
43674     }
43675     Roo.form.BasicForm.superclass.constructor.call(this);
43676 };
43677
43678 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43679     /**
43680      * @cfg {String} method
43681      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43682      */
43683     /**
43684      * @cfg {DataReader} reader
43685      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43686      * This is optional as there is built-in support for processing JSON.
43687      */
43688     /**
43689      * @cfg {DataReader} errorReader
43690      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43691      * This is completely optional as there is built-in support for processing JSON.
43692      */
43693     /**
43694      * @cfg {String} url
43695      * The URL to use for form actions if one isn't supplied in the action options.
43696      */
43697     /**
43698      * @cfg {Boolean} fileUpload
43699      * Set to true if this form is a file upload.
43700      */
43701      
43702     /**
43703      * @cfg {Object} baseParams
43704      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43705      */
43706      /**
43707      
43708     /**
43709      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43710      */
43711     timeout: 30,
43712
43713     // private
43714     activeAction : null,
43715
43716     /**
43717      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43718      * or setValues() data instead of when the form was first created.
43719      */
43720     trackResetOnLoad : false,
43721     
43722     
43723     /**
43724      * childForms - used for multi-tab forms
43725      * @type {Array}
43726      */
43727     childForms : false,
43728     
43729     /**
43730      * allItems - full list of fields.
43731      * @type {Array}
43732      */
43733     allItems : false,
43734     
43735     /**
43736      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43737      * element by passing it or its id or mask the form itself by passing in true.
43738      * @type Mixed
43739      */
43740     waitMsgTarget : false,
43741
43742     // private
43743     initEl : function(el){
43744         this.el = Roo.get(el);
43745         this.id = this.el.id || Roo.id();
43746         this.el.on('submit', this.onSubmit, this);
43747         this.el.addClass('x-form');
43748     },
43749
43750     // private
43751     onSubmit : function(e){
43752         e.stopEvent();
43753     },
43754
43755     /**
43756      * Returns true if client-side validation on the form is successful.
43757      * @return Boolean
43758      */
43759     isValid : function(){
43760         var valid = true;
43761         this.items.each(function(f){
43762            if(!f.validate()){
43763                valid = false;
43764            }
43765         });
43766         return valid;
43767     },
43768
43769     /**
43770      * Returns true if any fields in this form have changed since their original load.
43771      * @return Boolean
43772      */
43773     isDirty : function(){
43774         var dirty = false;
43775         this.items.each(function(f){
43776            if(f.isDirty()){
43777                dirty = true;
43778                return false;
43779            }
43780         });
43781         return dirty;
43782     },
43783
43784     /**
43785      * Performs a predefined action (submit or load) or custom actions you define on this form.
43786      * @param {String} actionName The name of the action type
43787      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43788      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43789      * accept other config options):
43790      * <pre>
43791 Property          Type             Description
43792 ----------------  ---------------  ----------------------------------------------------------------------------------
43793 url               String           The url for the action (defaults to the form's url)
43794 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43795 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43796 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43797                                    validate the form on the client (defaults to false)
43798      * </pre>
43799      * @return {BasicForm} this
43800      */
43801     doAction : function(action, options){
43802         if(typeof action == 'string'){
43803             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43804         }
43805         if(this.fireEvent('beforeaction', this, action) !== false){
43806             this.beforeAction(action);
43807             action.run.defer(100, action);
43808         }
43809         return this;
43810     },
43811
43812     /**
43813      * Shortcut to do a submit action.
43814      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43815      * @return {BasicForm} this
43816      */
43817     submit : function(options){
43818         this.doAction('submit', options);
43819         return this;
43820     },
43821
43822     /**
43823      * Shortcut to do a load action.
43824      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43825      * @return {BasicForm} this
43826      */
43827     load : function(options){
43828         this.doAction('load', options);
43829         return this;
43830     },
43831
43832     /**
43833      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43834      * @param {Record} record The record to edit
43835      * @return {BasicForm} this
43836      */
43837     updateRecord : function(record){
43838         record.beginEdit();
43839         var fs = record.fields;
43840         fs.each(function(f){
43841             var field = this.findField(f.name);
43842             if(field){
43843                 record.set(f.name, field.getValue());
43844             }
43845         }, this);
43846         record.endEdit();
43847         return this;
43848     },
43849
43850     /**
43851      * Loads an Roo.data.Record into this form.
43852      * @param {Record} record The record to load
43853      * @return {BasicForm} this
43854      */
43855     loadRecord : function(record){
43856         this.setValues(record.data);
43857         return this;
43858     },
43859
43860     // private
43861     beforeAction : function(action){
43862         var o = action.options;
43863         
43864        
43865         if(this.waitMsgTarget === true){
43866             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43867         }else if(this.waitMsgTarget){
43868             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43869             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43870         }else {
43871             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43872         }
43873          
43874     },
43875
43876     // private
43877     afterAction : function(action, success){
43878         this.activeAction = null;
43879         var o = action.options;
43880         
43881         if(this.waitMsgTarget === true){
43882             this.el.unmask();
43883         }else if(this.waitMsgTarget){
43884             this.waitMsgTarget.unmask();
43885         }else{
43886             Roo.MessageBox.updateProgress(1);
43887             Roo.MessageBox.hide();
43888         }
43889          
43890         if(success){
43891             if(o.reset){
43892                 this.reset();
43893             }
43894             Roo.callback(o.success, o.scope, [this, action]);
43895             this.fireEvent('actioncomplete', this, action);
43896             
43897         }else{
43898             
43899             // failure condition..
43900             // we have a scenario where updates need confirming.
43901             // eg. if a locking scenario exists..
43902             // we look for { errors : { needs_confirm : true }} in the response.
43903             if (
43904                 (typeof(action.result) != 'undefined')  &&
43905                 (typeof(action.result.errors) != 'undefined')  &&
43906                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43907            ){
43908                 var _t = this;
43909                 Roo.MessageBox.confirm(
43910                     "Change requires confirmation",
43911                     action.result.errorMsg,
43912                     function(r) {
43913                         if (r != 'yes') {
43914                             return;
43915                         }
43916                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43917                     }
43918                     
43919                 );
43920                 
43921                 
43922                 
43923                 return;
43924             }
43925             
43926             Roo.callback(o.failure, o.scope, [this, action]);
43927             // show an error message if no failed handler is set..
43928             if (!this.hasListener('actionfailed')) {
43929                 Roo.MessageBox.alert("Error",
43930                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43931                         action.result.errorMsg :
43932                         "Saving Failed, please check your entries or try again"
43933                 );
43934             }
43935             
43936             this.fireEvent('actionfailed', this, action);
43937         }
43938         
43939     },
43940
43941     /**
43942      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43943      * @param {String} id The value to search for
43944      * @return Field
43945      */
43946     findField : function(id){
43947         var field = this.items.get(id);
43948         if(!field){
43949             this.items.each(function(f){
43950                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43951                     field = f;
43952                     return false;
43953                 }
43954             });
43955         }
43956         return field || null;
43957     },
43958
43959     /**
43960      * Add a secondary form to this one, 
43961      * Used to provide tabbed forms. One form is primary, with hidden values 
43962      * which mirror the elements from the other forms.
43963      * 
43964      * @param {Roo.form.Form} form to add.
43965      * 
43966      */
43967     addForm : function(form)
43968     {
43969        
43970         if (this.childForms.indexOf(form) > -1) {
43971             // already added..
43972             return;
43973         }
43974         this.childForms.push(form);
43975         var n = '';
43976         Roo.each(form.allItems, function (fe) {
43977             
43978             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43979             if (this.findField(n)) { // already added..
43980                 return;
43981             }
43982             var add = new Roo.form.Hidden({
43983                 name : n
43984             });
43985             add.render(this.el);
43986             
43987             this.add( add );
43988         }, this);
43989         
43990     },
43991     /**
43992      * Mark fields in this form invalid in bulk.
43993      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43994      * @return {BasicForm} this
43995      */
43996     markInvalid : function(errors){
43997         if(errors instanceof Array){
43998             for(var i = 0, len = errors.length; i < len; i++){
43999                 var fieldError = errors[i];
44000                 var f = this.findField(fieldError.id);
44001                 if(f){
44002                     f.markInvalid(fieldError.msg);
44003                 }
44004             }
44005         }else{
44006             var field, id;
44007             for(id in errors){
44008                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44009                     field.markInvalid(errors[id]);
44010                 }
44011             }
44012         }
44013         Roo.each(this.childForms || [], function (f) {
44014             f.markInvalid(errors);
44015         });
44016         
44017         return this;
44018     },
44019
44020     /**
44021      * Set values for fields in this form in bulk.
44022      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44023      * @return {BasicForm} this
44024      */
44025     setValues : function(values){
44026         if(values instanceof Array){ // array of objects
44027             for(var i = 0, len = values.length; i < len; i++){
44028                 var v = values[i];
44029                 var f = this.findField(v.id);
44030                 if(f){
44031                     f.setValue(v.value);
44032                     if(this.trackResetOnLoad){
44033                         f.originalValue = f.getValue();
44034                     }
44035                 }
44036             }
44037         }else{ // object hash
44038             var field, id;
44039             for(id in values){
44040                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44041                     
44042                     if (field.setFromData && 
44043                         field.valueField && 
44044                         field.displayField &&
44045                         // combos' with local stores can 
44046                         // be queried via setValue()
44047                         // to set their value..
44048                         (field.store && !field.store.isLocal)
44049                         ) {
44050                         // it's a combo
44051                         var sd = { };
44052                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44053                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44054                         field.setFromData(sd);
44055                         
44056                     } else {
44057                         field.setValue(values[id]);
44058                     }
44059                     
44060                     
44061                     if(this.trackResetOnLoad){
44062                         field.originalValue = field.getValue();
44063                     }
44064                 }
44065             }
44066         }
44067          
44068         Roo.each(this.childForms || [], function (f) {
44069             f.setValues(values);
44070         });
44071                 
44072         return this;
44073     },
44074
44075     /**
44076      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44077      * they are returned as an array.
44078      * @param {Boolean} asString
44079      * @return {Object}
44080      */
44081     getValues : function(asString){
44082         if (this.childForms) {
44083             // copy values from the child forms
44084             Roo.each(this.childForms, function (f) {
44085                 this.setValues(f.getValues());
44086             }, this);
44087         }
44088         
44089         
44090         
44091         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44092         if(asString === true){
44093             return fs;
44094         }
44095         return Roo.urlDecode(fs);
44096     },
44097     
44098     /**
44099      * Returns the fields in this form as an object with key/value pairs. 
44100      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44101      * @return {Object}
44102      */
44103     getFieldValues : function(with_hidden)
44104     {
44105         if (this.childForms) {
44106             // copy values from the child forms
44107             // should this call getFieldValues - probably not as we do not currently copy
44108             // hidden fields when we generate..
44109             Roo.each(this.childForms, function (f) {
44110                 this.setValues(f.getValues());
44111             }, this);
44112         }
44113         
44114         var ret = {};
44115         this.items.each(function(f){
44116             if (!f.getName()) {
44117                 return;
44118             }
44119             var v = f.getValue();
44120             if (f.inputType =='radio') {
44121                 if (typeof(ret[f.getName()]) == 'undefined') {
44122                     ret[f.getName()] = ''; // empty..
44123                 }
44124                 
44125                 if (!f.el.dom.checked) {
44126                     return;
44127                     
44128                 }
44129                 v = f.el.dom.value;
44130                 
44131             }
44132             
44133             // not sure if this supported any more..
44134             if ((typeof(v) == 'object') && f.getRawValue) {
44135                 v = f.getRawValue() ; // dates..
44136             }
44137             // combo boxes where name != hiddenName...
44138             if (f.name != f.getName()) {
44139                 ret[f.name] = f.getRawValue();
44140             }
44141             ret[f.getName()] = v;
44142         });
44143         
44144         return ret;
44145     },
44146
44147     /**
44148      * Clears all invalid messages in this form.
44149      * @return {BasicForm} this
44150      */
44151     clearInvalid : function(){
44152         this.items.each(function(f){
44153            f.clearInvalid();
44154         });
44155         
44156         Roo.each(this.childForms || [], function (f) {
44157             f.clearInvalid();
44158         });
44159         
44160         
44161         return this;
44162     },
44163
44164     /**
44165      * Resets this form.
44166      * @return {BasicForm} this
44167      */
44168     reset : function(){
44169         this.items.each(function(f){
44170             f.reset();
44171         });
44172         
44173         Roo.each(this.childForms || [], function (f) {
44174             f.reset();
44175         });
44176        
44177         
44178         return this;
44179     },
44180
44181     /**
44182      * Add Roo.form components to this form.
44183      * @param {Field} field1
44184      * @param {Field} field2 (optional)
44185      * @param {Field} etc (optional)
44186      * @return {BasicForm} this
44187      */
44188     add : function(){
44189         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44190         return this;
44191     },
44192
44193
44194     /**
44195      * Removes a field from the items collection (does NOT remove its markup).
44196      * @param {Field} field
44197      * @return {BasicForm} this
44198      */
44199     remove : function(field){
44200         this.items.remove(field);
44201         return this;
44202     },
44203
44204     /**
44205      * Looks at the fields in this form, checks them for an id attribute,
44206      * and calls applyTo on the existing dom element with that id.
44207      * @return {BasicForm} this
44208      */
44209     render : function(){
44210         this.items.each(function(f){
44211             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44212                 f.applyTo(f.id);
44213             }
44214         });
44215         return this;
44216     },
44217
44218     /**
44219      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44220      * @param {Object} values
44221      * @return {BasicForm} this
44222      */
44223     applyToFields : function(o){
44224         this.items.each(function(f){
44225            Roo.apply(f, o);
44226         });
44227         return this;
44228     },
44229
44230     /**
44231      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44232      * @param {Object} values
44233      * @return {BasicForm} this
44234      */
44235     applyIfToFields : function(o){
44236         this.items.each(function(f){
44237            Roo.applyIf(f, o);
44238         });
44239         return this;
44240     }
44241 });
44242
44243 // back compat
44244 Roo.BasicForm = Roo.form.BasicForm;/*
44245  * Based on:
44246  * Ext JS Library 1.1.1
44247  * Copyright(c) 2006-2007, Ext JS, LLC.
44248  *
44249  * Originally Released Under LGPL - original licence link has changed is not relivant.
44250  *
44251  * Fork - LGPL
44252  * <script type="text/javascript">
44253  */
44254
44255 /**
44256  * @class Roo.form.Form
44257  * @extends Roo.form.BasicForm
44258  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44259  * @constructor
44260  * @param {Object} config Configuration options
44261  */
44262 Roo.form.Form = function(config){
44263     var xitems =  [];
44264     if (config.items) {
44265         xitems = config.items;
44266         delete config.items;
44267     }
44268    
44269     
44270     Roo.form.Form.superclass.constructor.call(this, null, config);
44271     this.url = this.url || this.action;
44272     if(!this.root){
44273         this.root = new Roo.form.Layout(Roo.applyIf({
44274             id: Roo.id()
44275         }, config));
44276     }
44277     this.active = this.root;
44278     /**
44279      * Array of all the buttons that have been added to this form via {@link addButton}
44280      * @type Array
44281      */
44282     this.buttons = [];
44283     this.allItems = [];
44284     this.addEvents({
44285         /**
44286          * @event clientvalidation
44287          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44288          * @param {Form} this
44289          * @param {Boolean} valid true if the form has passed client-side validation
44290          */
44291         clientvalidation: true,
44292         /**
44293          * @event rendered
44294          * Fires when the form is rendered
44295          * @param {Roo.form.Form} form
44296          */
44297         rendered : true
44298     });
44299     
44300     if (this.progressUrl) {
44301             // push a hidden field onto the list of fields..
44302             this.addxtype( {
44303                     xns: Roo.form, 
44304                     xtype : 'Hidden', 
44305                     name : 'UPLOAD_IDENTIFIER' 
44306             });
44307         }
44308         
44309     
44310     Roo.each(xitems, this.addxtype, this);
44311     
44312     
44313     
44314 };
44315
44316 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44317     /**
44318      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44319      */
44320     /**
44321      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44322      */
44323     /**
44324      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44325      */
44326     buttonAlign:'center',
44327
44328     /**
44329      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44330      */
44331     minButtonWidth:75,
44332
44333     /**
44334      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44335      * This property cascades to child containers if not set.
44336      */
44337     labelAlign:'left',
44338
44339     /**
44340      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44341      * fires a looping event with that state. This is required to bind buttons to the valid
44342      * state using the config value formBind:true on the button.
44343      */
44344     monitorValid : false,
44345
44346     /**
44347      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44348      */
44349     monitorPoll : 200,
44350     
44351     /**
44352      * @cfg {String} progressUrl - Url to return progress data 
44353      */
44354     
44355     progressUrl : false,
44356   
44357     /**
44358      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44359      * fields are added and the column is closed. If no fields are passed the column remains open
44360      * until end() is called.
44361      * @param {Object} config The config to pass to the column
44362      * @param {Field} field1 (optional)
44363      * @param {Field} field2 (optional)
44364      * @param {Field} etc (optional)
44365      * @return Column The column container object
44366      */
44367     column : function(c){
44368         var col = new Roo.form.Column(c);
44369         this.start(col);
44370         if(arguments.length > 1){ // duplicate code required because of Opera
44371             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44372             this.end();
44373         }
44374         return col;
44375     },
44376
44377     /**
44378      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44379      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44380      * until end() is called.
44381      * @param {Object} config The config to pass to the fieldset
44382      * @param {Field} field1 (optional)
44383      * @param {Field} field2 (optional)
44384      * @param {Field} etc (optional)
44385      * @return FieldSet The fieldset container object
44386      */
44387     fieldset : function(c){
44388         var fs = new Roo.form.FieldSet(c);
44389         this.start(fs);
44390         if(arguments.length > 1){ // duplicate code required because of Opera
44391             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44392             this.end();
44393         }
44394         return fs;
44395     },
44396
44397     /**
44398      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44399      * fields are added and the container is closed. If no fields are passed the container remains open
44400      * until end() is called.
44401      * @param {Object} config The config to pass to the Layout
44402      * @param {Field} field1 (optional)
44403      * @param {Field} field2 (optional)
44404      * @param {Field} etc (optional)
44405      * @return Layout The container object
44406      */
44407     container : function(c){
44408         var l = new Roo.form.Layout(c);
44409         this.start(l);
44410         if(arguments.length > 1){ // duplicate code required because of Opera
44411             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44412             this.end();
44413         }
44414         return l;
44415     },
44416
44417     /**
44418      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44419      * @param {Object} container A Roo.form.Layout or subclass of Layout
44420      * @return {Form} this
44421      */
44422     start : function(c){
44423         // cascade label info
44424         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44425         this.active.stack.push(c);
44426         c.ownerCt = this.active;
44427         this.active = c;
44428         return this;
44429     },
44430
44431     /**
44432      * Closes the current open container
44433      * @return {Form} this
44434      */
44435     end : function(){
44436         if(this.active == this.root){
44437             return this;
44438         }
44439         this.active = this.active.ownerCt;
44440         return this;
44441     },
44442
44443     /**
44444      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44445      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44446      * as the label of the field.
44447      * @param {Field} field1
44448      * @param {Field} field2 (optional)
44449      * @param {Field} etc. (optional)
44450      * @return {Form} this
44451      */
44452     add : function(){
44453         this.active.stack.push.apply(this.active.stack, arguments);
44454         this.allItems.push.apply(this.allItems,arguments);
44455         var r = [];
44456         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44457             if(a[i].isFormField){
44458                 r.push(a[i]);
44459             }
44460         }
44461         if(r.length > 0){
44462             Roo.form.Form.superclass.add.apply(this, r);
44463         }
44464         return this;
44465     },
44466     
44467
44468     
44469     
44470     
44471      /**
44472      * Find any element that has been added to a form, using it's ID or name
44473      * This can include framesets, columns etc. along with regular fields..
44474      * @param {String} id - id or name to find.
44475      
44476      * @return {Element} e - or false if nothing found.
44477      */
44478     findbyId : function(id)
44479     {
44480         var ret = false;
44481         if (!id) {
44482             return ret;
44483         }
44484         Roo.each(this.allItems, function(f){
44485             if (f.id == id || f.name == id ){
44486                 ret = f;
44487                 return false;
44488             }
44489         });
44490         return ret;
44491     },
44492
44493     
44494     
44495     /**
44496      * Render this form into the passed container. This should only be called once!
44497      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44498      * @return {Form} this
44499      */
44500     render : function(ct)
44501     {
44502         
44503         
44504         
44505         ct = Roo.get(ct);
44506         var o = this.autoCreate || {
44507             tag: 'form',
44508             method : this.method || 'POST',
44509             id : this.id || Roo.id()
44510         };
44511         this.initEl(ct.createChild(o));
44512
44513         this.root.render(this.el);
44514         
44515        
44516              
44517         this.items.each(function(f){
44518             f.render('x-form-el-'+f.id);
44519         });
44520
44521         if(this.buttons.length > 0){
44522             // tables are required to maintain order and for correct IE layout
44523             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44524                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44525                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44526             }}, null, true);
44527             var tr = tb.getElementsByTagName('tr')[0];
44528             for(var i = 0, len = this.buttons.length; i < len; i++) {
44529                 var b = this.buttons[i];
44530                 var td = document.createElement('td');
44531                 td.className = 'x-form-btn-td';
44532                 b.render(tr.appendChild(td));
44533             }
44534         }
44535         if(this.monitorValid){ // initialize after render
44536             this.startMonitoring();
44537         }
44538         this.fireEvent('rendered', this);
44539         return this;
44540     },
44541
44542     /**
44543      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44544      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44545      * object or a valid Roo.DomHelper element config
44546      * @param {Function} handler The function called when the button is clicked
44547      * @param {Object} scope (optional) The scope of the handler function
44548      * @return {Roo.Button}
44549      */
44550     addButton : function(config, handler, scope){
44551         var bc = {
44552             handler: handler,
44553             scope: scope,
44554             minWidth: this.minButtonWidth,
44555             hideParent:true
44556         };
44557         if(typeof config == "string"){
44558             bc.text = config;
44559         }else{
44560             Roo.apply(bc, config);
44561         }
44562         var btn = new Roo.Button(null, bc);
44563         this.buttons.push(btn);
44564         return btn;
44565     },
44566
44567      /**
44568      * Adds a series of form elements (using the xtype property as the factory method.
44569      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44570      * @param {Object} config 
44571      */
44572     
44573     addxtype : function()
44574     {
44575         var ar = Array.prototype.slice.call(arguments, 0);
44576         var ret = false;
44577         for(var i = 0; i < ar.length; i++) {
44578             if (!ar[i]) {
44579                 continue; // skip -- if this happends something invalid got sent, we 
44580                 // should ignore it, as basically that interface element will not show up
44581                 // and that should be pretty obvious!!
44582             }
44583             
44584             if (Roo.form[ar[i].xtype]) {
44585                 ar[i].form = this;
44586                 var fe = Roo.factory(ar[i], Roo.form);
44587                 if (!ret) {
44588                     ret = fe;
44589                 }
44590                 fe.form = this;
44591                 if (fe.store) {
44592                     fe.store.form = this;
44593                 }
44594                 if (fe.isLayout) {  
44595                          
44596                     this.start(fe);
44597                     this.allItems.push(fe);
44598                     if (fe.items && fe.addxtype) {
44599                         fe.addxtype.apply(fe, fe.items);
44600                         delete fe.items;
44601                     }
44602                      this.end();
44603                     continue;
44604                 }
44605                 
44606                 
44607                  
44608                 this.add(fe);
44609               //  console.log('adding ' + ar[i].xtype);
44610             }
44611             if (ar[i].xtype == 'Button') {  
44612                 //console.log('adding button');
44613                 //console.log(ar[i]);
44614                 this.addButton(ar[i]);
44615                 this.allItems.push(fe);
44616                 continue;
44617             }
44618             
44619             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44620                 alert('end is not supported on xtype any more, use items');
44621             //    this.end();
44622             //    //console.log('adding end');
44623             }
44624             
44625         }
44626         return ret;
44627     },
44628     
44629     /**
44630      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44631      * option "monitorValid"
44632      */
44633     startMonitoring : function(){
44634         if(!this.bound){
44635             this.bound = true;
44636             Roo.TaskMgr.start({
44637                 run : this.bindHandler,
44638                 interval : this.monitorPoll || 200,
44639                 scope: this
44640             });
44641         }
44642     },
44643
44644     /**
44645      * Stops monitoring of the valid state of this form
44646      */
44647     stopMonitoring : function(){
44648         this.bound = false;
44649     },
44650
44651     // private
44652     bindHandler : function(){
44653         if(!this.bound){
44654             return false; // stops binding
44655         }
44656         var valid = true;
44657         this.items.each(function(f){
44658             if(!f.isValid(true)){
44659                 valid = false;
44660                 return false;
44661             }
44662         });
44663         for(var i = 0, len = this.buttons.length; i < len; i++){
44664             var btn = this.buttons[i];
44665             if(btn.formBind === true && btn.disabled === valid){
44666                 btn.setDisabled(!valid);
44667             }
44668         }
44669         this.fireEvent('clientvalidation', this, valid);
44670     }
44671     
44672     
44673     
44674     
44675     
44676     
44677     
44678     
44679 });
44680
44681
44682 // back compat
44683 Roo.Form = Roo.form.Form;
44684 /*
44685  * Based on:
44686  * Ext JS Library 1.1.1
44687  * Copyright(c) 2006-2007, Ext JS, LLC.
44688  *
44689  * Originally Released Under LGPL - original licence link has changed is not relivant.
44690  *
44691  * Fork - LGPL
44692  * <script type="text/javascript">
44693  */
44694
44695 // as we use this in bootstrap.
44696 Roo.namespace('Roo.form');
44697  /**
44698  * @class Roo.form.Action
44699  * Internal Class used to handle form actions
44700  * @constructor
44701  * @param {Roo.form.BasicForm} el The form element or its id
44702  * @param {Object} config Configuration options
44703  */
44704
44705  
44706  
44707 // define the action interface
44708 Roo.form.Action = function(form, options){
44709     this.form = form;
44710     this.options = options || {};
44711 };
44712 /**
44713  * Client Validation Failed
44714  * @const 
44715  */
44716 Roo.form.Action.CLIENT_INVALID = 'client';
44717 /**
44718  * Server Validation Failed
44719  * @const 
44720  */
44721 Roo.form.Action.SERVER_INVALID = 'server';
44722  /**
44723  * Connect to Server Failed
44724  * @const 
44725  */
44726 Roo.form.Action.CONNECT_FAILURE = 'connect';
44727 /**
44728  * Reading Data from Server Failed
44729  * @const 
44730  */
44731 Roo.form.Action.LOAD_FAILURE = 'load';
44732
44733 Roo.form.Action.prototype = {
44734     type : 'default',
44735     failureType : undefined,
44736     response : undefined,
44737     result : undefined,
44738
44739     // interface method
44740     run : function(options){
44741
44742     },
44743
44744     // interface method
44745     success : function(response){
44746
44747     },
44748
44749     // interface method
44750     handleResponse : function(response){
44751
44752     },
44753
44754     // default connection failure
44755     failure : function(response){
44756         
44757         this.response = response;
44758         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44759         this.form.afterAction(this, false);
44760     },
44761
44762     processResponse : function(response){
44763         this.response = response;
44764         if(!response.responseText){
44765             return true;
44766         }
44767         this.result = this.handleResponse(response);
44768         return this.result;
44769     },
44770
44771     // utility functions used internally
44772     getUrl : function(appendParams){
44773         var url = this.options.url || this.form.url || this.form.el.dom.action;
44774         if(appendParams){
44775             var p = this.getParams();
44776             if(p){
44777                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44778             }
44779         }
44780         return url;
44781     },
44782
44783     getMethod : function(){
44784         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44785     },
44786
44787     getParams : function(){
44788         var bp = this.form.baseParams;
44789         var p = this.options.params;
44790         if(p){
44791             if(typeof p == "object"){
44792                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44793             }else if(typeof p == 'string' && bp){
44794                 p += '&' + Roo.urlEncode(bp);
44795             }
44796         }else if(bp){
44797             p = Roo.urlEncode(bp);
44798         }
44799         return p;
44800     },
44801
44802     createCallback : function(){
44803         return {
44804             success: this.success,
44805             failure: this.failure,
44806             scope: this,
44807             timeout: (this.form.timeout*1000),
44808             upload: this.form.fileUpload ? this.success : undefined
44809         };
44810     }
44811 };
44812
44813 Roo.form.Action.Submit = function(form, options){
44814     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44815 };
44816
44817 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44818     type : 'submit',
44819
44820     haveProgress : false,
44821     uploadComplete : false,
44822     
44823     // uploadProgress indicator.
44824     uploadProgress : function()
44825     {
44826         if (!this.form.progressUrl) {
44827             return;
44828         }
44829         
44830         if (!this.haveProgress) {
44831             Roo.MessageBox.progress("Uploading", "Uploading");
44832         }
44833         if (this.uploadComplete) {
44834            Roo.MessageBox.hide();
44835            return;
44836         }
44837         
44838         this.haveProgress = true;
44839    
44840         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44841         
44842         var c = new Roo.data.Connection();
44843         c.request({
44844             url : this.form.progressUrl,
44845             params: {
44846                 id : uid
44847             },
44848             method: 'GET',
44849             success : function(req){
44850                //console.log(data);
44851                 var rdata = false;
44852                 var edata;
44853                 try  {
44854                    rdata = Roo.decode(req.responseText)
44855                 } catch (e) {
44856                     Roo.log("Invalid data from server..");
44857                     Roo.log(edata);
44858                     return;
44859                 }
44860                 if (!rdata || !rdata.success) {
44861                     Roo.log(rdata);
44862                     Roo.MessageBox.alert(Roo.encode(rdata));
44863                     return;
44864                 }
44865                 var data = rdata.data;
44866                 
44867                 if (this.uploadComplete) {
44868                    Roo.MessageBox.hide();
44869                    return;
44870                 }
44871                    
44872                 if (data){
44873                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44874                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44875                     );
44876                 }
44877                 this.uploadProgress.defer(2000,this);
44878             },
44879        
44880             failure: function(data) {
44881                 Roo.log('progress url failed ');
44882                 Roo.log(data);
44883             },
44884             scope : this
44885         });
44886            
44887     },
44888     
44889     
44890     run : function()
44891     {
44892         // run get Values on the form, so it syncs any secondary forms.
44893         this.form.getValues();
44894         
44895         var o = this.options;
44896         var method = this.getMethod();
44897         var isPost = method == 'POST';
44898         if(o.clientValidation === false || this.form.isValid()){
44899             
44900             if (this.form.progressUrl) {
44901                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44902                     (new Date() * 1) + '' + Math.random());
44903                     
44904             } 
44905             
44906             
44907             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44908                 form:this.form.el.dom,
44909                 url:this.getUrl(!isPost),
44910                 method: method,
44911                 params:isPost ? this.getParams() : null,
44912                 isUpload: this.form.fileUpload
44913             }));
44914             
44915             this.uploadProgress();
44916
44917         }else if (o.clientValidation !== false){ // client validation failed
44918             this.failureType = Roo.form.Action.CLIENT_INVALID;
44919             this.form.afterAction(this, false);
44920         }
44921     },
44922
44923     success : function(response)
44924     {
44925         this.uploadComplete= true;
44926         if (this.haveProgress) {
44927             Roo.MessageBox.hide();
44928         }
44929         
44930         
44931         var result = this.processResponse(response);
44932         if(result === true || result.success){
44933             this.form.afterAction(this, true);
44934             return;
44935         }
44936         if(result.errors){
44937             this.form.markInvalid(result.errors);
44938             this.failureType = Roo.form.Action.SERVER_INVALID;
44939         }
44940         this.form.afterAction(this, false);
44941     },
44942     failure : function(response)
44943     {
44944         this.uploadComplete= true;
44945         if (this.haveProgress) {
44946             Roo.MessageBox.hide();
44947         }
44948         
44949         this.response = response;
44950         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44951         this.form.afterAction(this, false);
44952     },
44953     
44954     handleResponse : function(response){
44955         if(this.form.errorReader){
44956             var rs = this.form.errorReader.read(response);
44957             var errors = [];
44958             if(rs.records){
44959                 for(var i = 0, len = rs.records.length; i < len; i++) {
44960                     var r = rs.records[i];
44961                     errors[i] = r.data;
44962                 }
44963             }
44964             if(errors.length < 1){
44965                 errors = null;
44966             }
44967             return {
44968                 success : rs.success,
44969                 errors : errors
44970             };
44971         }
44972         var ret = false;
44973         try {
44974             ret = Roo.decode(response.responseText);
44975         } catch (e) {
44976             ret = {
44977                 success: false,
44978                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44979                 errors : []
44980             };
44981         }
44982         return ret;
44983         
44984     }
44985 });
44986
44987
44988 Roo.form.Action.Load = function(form, options){
44989     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44990     this.reader = this.form.reader;
44991 };
44992
44993 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44994     type : 'load',
44995
44996     run : function(){
44997         
44998         Roo.Ajax.request(Roo.apply(
44999                 this.createCallback(), {
45000                     method:this.getMethod(),
45001                     url:this.getUrl(false),
45002                     params:this.getParams()
45003         }));
45004     },
45005
45006     success : function(response){
45007         
45008         var result = this.processResponse(response);
45009         if(result === true || !result.success || !result.data){
45010             this.failureType = Roo.form.Action.LOAD_FAILURE;
45011             this.form.afterAction(this, false);
45012             return;
45013         }
45014         this.form.clearInvalid();
45015         this.form.setValues(result.data);
45016         this.form.afterAction(this, true);
45017     },
45018
45019     handleResponse : function(response){
45020         if(this.form.reader){
45021             var rs = this.form.reader.read(response);
45022             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45023             return {
45024                 success : rs.success,
45025                 data : data
45026             };
45027         }
45028         return Roo.decode(response.responseText);
45029     }
45030 });
45031
45032 Roo.form.Action.ACTION_TYPES = {
45033     'load' : Roo.form.Action.Load,
45034     'submit' : Roo.form.Action.Submit
45035 };/*
45036  * Based on:
45037  * Ext JS Library 1.1.1
45038  * Copyright(c) 2006-2007, Ext JS, LLC.
45039  *
45040  * Originally Released Under LGPL - original licence link has changed is not relivant.
45041  *
45042  * Fork - LGPL
45043  * <script type="text/javascript">
45044  */
45045  
45046 /**
45047  * @class Roo.form.Layout
45048  * @extends Roo.Component
45049  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45050  * @constructor
45051  * @param {Object} config Configuration options
45052  */
45053 Roo.form.Layout = function(config){
45054     var xitems = [];
45055     if (config.items) {
45056         xitems = config.items;
45057         delete config.items;
45058     }
45059     Roo.form.Layout.superclass.constructor.call(this, config);
45060     this.stack = [];
45061     Roo.each(xitems, this.addxtype, this);
45062      
45063 };
45064
45065 Roo.extend(Roo.form.Layout, Roo.Component, {
45066     /**
45067      * @cfg {String/Object} autoCreate
45068      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45069      */
45070     /**
45071      * @cfg {String/Object/Function} style
45072      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45073      * a function which returns such a specification.
45074      */
45075     /**
45076      * @cfg {String} labelAlign
45077      * Valid values are "left," "top" and "right" (defaults to "left")
45078      */
45079     /**
45080      * @cfg {Number} labelWidth
45081      * Fixed width in pixels of all field labels (defaults to undefined)
45082      */
45083     /**
45084      * @cfg {Boolean} clear
45085      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45086      */
45087     clear : true,
45088     /**
45089      * @cfg {String} labelSeparator
45090      * The separator to use after field labels (defaults to ':')
45091      */
45092     labelSeparator : ':',
45093     /**
45094      * @cfg {Boolean} hideLabels
45095      * True to suppress the display of field labels in this layout (defaults to false)
45096      */
45097     hideLabels : false,
45098
45099     // private
45100     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45101     
45102     isLayout : true,
45103     
45104     // private
45105     onRender : function(ct, position){
45106         if(this.el){ // from markup
45107             this.el = Roo.get(this.el);
45108         }else {  // generate
45109             var cfg = this.getAutoCreate();
45110             this.el = ct.createChild(cfg, position);
45111         }
45112         if(this.style){
45113             this.el.applyStyles(this.style);
45114         }
45115         if(this.labelAlign){
45116             this.el.addClass('x-form-label-'+this.labelAlign);
45117         }
45118         if(this.hideLabels){
45119             this.labelStyle = "display:none";
45120             this.elementStyle = "padding-left:0;";
45121         }else{
45122             if(typeof this.labelWidth == 'number'){
45123                 this.labelStyle = "width:"+this.labelWidth+"px;";
45124                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45125             }
45126             if(this.labelAlign == 'top'){
45127                 this.labelStyle = "width:auto;";
45128                 this.elementStyle = "padding-left:0;";
45129             }
45130         }
45131         var stack = this.stack;
45132         var slen = stack.length;
45133         if(slen > 0){
45134             if(!this.fieldTpl){
45135                 var t = new Roo.Template(
45136                     '<div class="x-form-item {5}">',
45137                         '<label for="{0}" style="{2}">{1}{4}</label>',
45138                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45139                         '</div>',
45140                     '</div><div class="x-form-clear-left"></div>'
45141                 );
45142                 t.disableFormats = true;
45143                 t.compile();
45144                 Roo.form.Layout.prototype.fieldTpl = t;
45145             }
45146             for(var i = 0; i < slen; i++) {
45147                 if(stack[i].isFormField){
45148                     this.renderField(stack[i]);
45149                 }else{
45150                     this.renderComponent(stack[i]);
45151                 }
45152             }
45153         }
45154         if(this.clear){
45155             this.el.createChild({cls:'x-form-clear'});
45156         }
45157     },
45158
45159     // private
45160     renderField : function(f){
45161         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45162                f.id, //0
45163                f.fieldLabel, //1
45164                f.labelStyle||this.labelStyle||'', //2
45165                this.elementStyle||'', //3
45166                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45167                f.itemCls||this.itemCls||''  //5
45168        ], true).getPrevSibling());
45169     },
45170
45171     // private
45172     renderComponent : function(c){
45173         c.render(c.isLayout ? this.el : this.el.createChild());    
45174     },
45175     /**
45176      * Adds a object form elements (using the xtype property as the factory method.)
45177      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45178      * @param {Object} config 
45179      */
45180     addxtype : function(o)
45181     {
45182         // create the lement.
45183         o.form = this.form;
45184         var fe = Roo.factory(o, Roo.form);
45185         this.form.allItems.push(fe);
45186         this.stack.push(fe);
45187         
45188         if (fe.isFormField) {
45189             this.form.items.add(fe);
45190         }
45191          
45192         return fe;
45193     }
45194 });
45195
45196 /**
45197  * @class Roo.form.Column
45198  * @extends Roo.form.Layout
45199  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45200  * @constructor
45201  * @param {Object} config Configuration options
45202  */
45203 Roo.form.Column = function(config){
45204     Roo.form.Column.superclass.constructor.call(this, config);
45205 };
45206
45207 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45208     /**
45209      * @cfg {Number/String} width
45210      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45211      */
45212     /**
45213      * @cfg {String/Object} autoCreate
45214      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45215      */
45216
45217     // private
45218     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45219
45220     // private
45221     onRender : function(ct, position){
45222         Roo.form.Column.superclass.onRender.call(this, ct, position);
45223         if(this.width){
45224             this.el.setWidth(this.width);
45225         }
45226     }
45227 });
45228
45229
45230 /**
45231  * @class Roo.form.Row
45232  * @extends Roo.form.Layout
45233  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45234  * @constructor
45235  * @param {Object} config Configuration options
45236  */
45237
45238  
45239 Roo.form.Row = function(config){
45240     Roo.form.Row.superclass.constructor.call(this, config);
45241 };
45242  
45243 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45244       /**
45245      * @cfg {Number/String} width
45246      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45247      */
45248     /**
45249      * @cfg {Number/String} height
45250      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45251      */
45252     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45253     
45254     padWidth : 20,
45255     // private
45256     onRender : function(ct, position){
45257         //console.log('row render');
45258         if(!this.rowTpl){
45259             var t = new Roo.Template(
45260                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45261                     '<label for="{0}" style="{2}">{1}{4}</label>',
45262                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45263                     '</div>',
45264                 '</div>'
45265             );
45266             t.disableFormats = true;
45267             t.compile();
45268             Roo.form.Layout.prototype.rowTpl = t;
45269         }
45270         this.fieldTpl = this.rowTpl;
45271         
45272         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45273         var labelWidth = 100;
45274         
45275         if ((this.labelAlign != 'top')) {
45276             if (typeof this.labelWidth == 'number') {
45277                 labelWidth = this.labelWidth
45278             }
45279             this.padWidth =  20 + labelWidth;
45280             
45281         }
45282         
45283         Roo.form.Column.superclass.onRender.call(this, ct, position);
45284         if(this.width){
45285             this.el.setWidth(this.width);
45286         }
45287         if(this.height){
45288             this.el.setHeight(this.height);
45289         }
45290     },
45291     
45292     // private
45293     renderField : function(f){
45294         f.fieldEl = this.fieldTpl.append(this.el, [
45295                f.id, f.fieldLabel,
45296                f.labelStyle||this.labelStyle||'',
45297                this.elementStyle||'',
45298                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45299                f.itemCls||this.itemCls||'',
45300                f.width ? f.width + this.padWidth : 160 + this.padWidth
45301        ],true);
45302     }
45303 });
45304  
45305
45306 /**
45307  * @class Roo.form.FieldSet
45308  * @extends Roo.form.Layout
45309  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45310  * @constructor
45311  * @param {Object} config Configuration options
45312  */
45313 Roo.form.FieldSet = function(config){
45314     Roo.form.FieldSet.superclass.constructor.call(this, config);
45315 };
45316
45317 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45318     /**
45319      * @cfg {String} legend
45320      * The text to display as the legend for the FieldSet (defaults to '')
45321      */
45322     /**
45323      * @cfg {String/Object} autoCreate
45324      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45325      */
45326
45327     // private
45328     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45329
45330     // private
45331     onRender : function(ct, position){
45332         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45333         if(this.legend){
45334             this.setLegend(this.legend);
45335         }
45336     },
45337
45338     // private
45339     setLegend : function(text){
45340         if(this.rendered){
45341             this.el.child('legend').update(text);
45342         }
45343     }
45344 });/*
45345  * Based on:
45346  * Ext JS Library 1.1.1
45347  * Copyright(c) 2006-2007, Ext JS, LLC.
45348  *
45349  * Originally Released Under LGPL - original licence link has changed is not relivant.
45350  *
45351  * Fork - LGPL
45352  * <script type="text/javascript">
45353  */
45354 /**
45355  * @class Roo.form.VTypes
45356  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45357  * @singleton
45358  */
45359 Roo.form.VTypes = function(){
45360     // closure these in so they are only created once.
45361     var alpha = /^[a-zA-Z_]+$/;
45362     var alphanum = /^[a-zA-Z0-9_]+$/;
45363     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45364     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45365
45366     // All these messages and functions are configurable
45367     return {
45368         /**
45369          * The function used to validate email addresses
45370          * @param {String} value The email address
45371          */
45372         'email' : function(v){
45373             return email.test(v);
45374         },
45375         /**
45376          * The error text to display when the email validation function returns false
45377          * @type String
45378          */
45379         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45380         /**
45381          * The keystroke filter mask to be applied on email input
45382          * @type RegExp
45383          */
45384         'emailMask' : /[a-z0-9_\.\-@]/i,
45385
45386         /**
45387          * The function used to validate URLs
45388          * @param {String} value The URL
45389          */
45390         'url' : function(v){
45391             return url.test(v);
45392         },
45393         /**
45394          * The error text to display when the url validation function returns false
45395          * @type String
45396          */
45397         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45398         
45399         /**
45400          * The function used to validate alpha values
45401          * @param {String} value The value
45402          */
45403         'alpha' : function(v){
45404             return alpha.test(v);
45405         },
45406         /**
45407          * The error text to display when the alpha validation function returns false
45408          * @type String
45409          */
45410         'alphaText' : 'This field should only contain letters and _',
45411         /**
45412          * The keystroke filter mask to be applied on alpha input
45413          * @type RegExp
45414          */
45415         'alphaMask' : /[a-z_]/i,
45416
45417         /**
45418          * The function used to validate alphanumeric values
45419          * @param {String} value The value
45420          */
45421         'alphanum' : function(v){
45422             return alphanum.test(v);
45423         },
45424         /**
45425          * The error text to display when the alphanumeric validation function returns false
45426          * @type String
45427          */
45428         'alphanumText' : 'This field should only contain letters, numbers and _',
45429         /**
45430          * The keystroke filter mask to be applied on alphanumeric input
45431          * @type RegExp
45432          */
45433         'alphanumMask' : /[a-z0-9_]/i
45434     };
45435 }();//<script type="text/javascript">
45436
45437 /**
45438  * @class Roo.form.FCKeditor
45439  * @extends Roo.form.TextArea
45440  * Wrapper around the FCKEditor http://www.fckeditor.net
45441  * @constructor
45442  * Creates a new FCKeditor
45443  * @param {Object} config Configuration options
45444  */
45445 Roo.form.FCKeditor = function(config){
45446     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45447     this.addEvents({
45448          /**
45449          * @event editorinit
45450          * Fired when the editor is initialized - you can add extra handlers here..
45451          * @param {FCKeditor} this
45452          * @param {Object} the FCK object.
45453          */
45454         editorinit : true
45455     });
45456     
45457     
45458 };
45459 Roo.form.FCKeditor.editors = { };
45460 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45461 {
45462     //defaultAutoCreate : {
45463     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45464     //},
45465     // private
45466     /**
45467      * @cfg {Object} fck options - see fck manual for details.
45468      */
45469     fckconfig : false,
45470     
45471     /**
45472      * @cfg {Object} fck toolbar set (Basic or Default)
45473      */
45474     toolbarSet : 'Basic',
45475     /**
45476      * @cfg {Object} fck BasePath
45477      */ 
45478     basePath : '/fckeditor/',
45479     
45480     
45481     frame : false,
45482     
45483     value : '',
45484     
45485    
45486     onRender : function(ct, position)
45487     {
45488         if(!this.el){
45489             this.defaultAutoCreate = {
45490                 tag: "textarea",
45491                 style:"width:300px;height:60px;",
45492                 autocomplete: "off"
45493             };
45494         }
45495         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45496         /*
45497         if(this.grow){
45498             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45499             if(this.preventScrollbars){
45500                 this.el.setStyle("overflow", "hidden");
45501             }
45502             this.el.setHeight(this.growMin);
45503         }
45504         */
45505         //console.log('onrender' + this.getId() );
45506         Roo.form.FCKeditor.editors[this.getId()] = this;
45507          
45508
45509         this.replaceTextarea() ;
45510         
45511     },
45512     
45513     getEditor : function() {
45514         return this.fckEditor;
45515     },
45516     /**
45517      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45518      * @param {Mixed} value The value to set
45519      */
45520     
45521     
45522     setValue : function(value)
45523     {
45524         //console.log('setValue: ' + value);
45525         
45526         if(typeof(value) == 'undefined') { // not sure why this is happending...
45527             return;
45528         }
45529         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45530         
45531         //if(!this.el || !this.getEditor()) {
45532         //    this.value = value;
45533             //this.setValue.defer(100,this,[value]);    
45534         //    return;
45535         //} 
45536         
45537         if(!this.getEditor()) {
45538             return;
45539         }
45540         
45541         this.getEditor().SetData(value);
45542         
45543         //
45544
45545     },
45546
45547     /**
45548      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45549      * @return {Mixed} value The field value
45550      */
45551     getValue : function()
45552     {
45553         
45554         if (this.frame && this.frame.dom.style.display == 'none') {
45555             return Roo.form.FCKeditor.superclass.getValue.call(this);
45556         }
45557         
45558         if(!this.el || !this.getEditor()) {
45559            
45560            // this.getValue.defer(100,this); 
45561             return this.value;
45562         }
45563        
45564         
45565         var value=this.getEditor().GetData();
45566         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45567         return Roo.form.FCKeditor.superclass.getValue.call(this);
45568         
45569
45570     },
45571
45572     /**
45573      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45574      * @return {Mixed} value The field value
45575      */
45576     getRawValue : function()
45577     {
45578         if (this.frame && this.frame.dom.style.display == 'none') {
45579             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45580         }
45581         
45582         if(!this.el || !this.getEditor()) {
45583             //this.getRawValue.defer(100,this); 
45584             return this.value;
45585             return;
45586         }
45587         
45588         
45589         
45590         var value=this.getEditor().GetData();
45591         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45592         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45593          
45594     },
45595     
45596     setSize : function(w,h) {
45597         
45598         
45599         
45600         //if (this.frame && this.frame.dom.style.display == 'none') {
45601         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45602         //    return;
45603         //}
45604         //if(!this.el || !this.getEditor()) {
45605         //    this.setSize.defer(100,this, [w,h]); 
45606         //    return;
45607         //}
45608         
45609         
45610         
45611         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45612         
45613         this.frame.dom.setAttribute('width', w);
45614         this.frame.dom.setAttribute('height', h);
45615         this.frame.setSize(w,h);
45616         
45617     },
45618     
45619     toggleSourceEdit : function(value) {
45620         
45621       
45622          
45623         this.el.dom.style.display = value ? '' : 'none';
45624         this.frame.dom.style.display = value ?  'none' : '';
45625         
45626     },
45627     
45628     
45629     focus: function(tag)
45630     {
45631         if (this.frame.dom.style.display == 'none') {
45632             return Roo.form.FCKeditor.superclass.focus.call(this);
45633         }
45634         if(!this.el || !this.getEditor()) {
45635             this.focus.defer(100,this, [tag]); 
45636             return;
45637         }
45638         
45639         
45640         
45641         
45642         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45643         this.getEditor().Focus();
45644         if (tgs.length) {
45645             if (!this.getEditor().Selection.GetSelection()) {
45646                 this.focus.defer(100,this, [tag]); 
45647                 return;
45648             }
45649             
45650             
45651             var r = this.getEditor().EditorDocument.createRange();
45652             r.setStart(tgs[0],0);
45653             r.setEnd(tgs[0],0);
45654             this.getEditor().Selection.GetSelection().removeAllRanges();
45655             this.getEditor().Selection.GetSelection().addRange(r);
45656             this.getEditor().Focus();
45657         }
45658         
45659     },
45660     
45661     
45662     
45663     replaceTextarea : function()
45664     {
45665         if ( document.getElementById( this.getId() + '___Frame' ) )
45666             return ;
45667         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45668         //{
45669             // We must check the elements firstly using the Id and then the name.
45670         var oTextarea = document.getElementById( this.getId() );
45671         
45672         var colElementsByName = document.getElementsByName( this.getId() ) ;
45673          
45674         oTextarea.style.display = 'none' ;
45675
45676         if ( oTextarea.tabIndex ) {            
45677             this.TabIndex = oTextarea.tabIndex ;
45678         }
45679         
45680         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45681         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45682         this.frame = Roo.get(this.getId() + '___Frame')
45683     },
45684     
45685     _getConfigHtml : function()
45686     {
45687         var sConfig = '' ;
45688
45689         for ( var o in this.fckconfig ) {
45690             sConfig += sConfig.length > 0  ? '&amp;' : '';
45691             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45692         }
45693
45694         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45695     },
45696     
45697     
45698     _getIFrameHtml : function()
45699     {
45700         var sFile = 'fckeditor.html' ;
45701         /* no idea what this is about..
45702         try
45703         {
45704             if ( (/fcksource=true/i).test( window.top.location.search ) )
45705                 sFile = 'fckeditor.original.html' ;
45706         }
45707         catch (e) { 
45708         */
45709
45710         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45711         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45712         
45713         
45714         var html = '<iframe id="' + this.getId() +
45715             '___Frame" src="' + sLink +
45716             '" width="' + this.width +
45717             '" height="' + this.height + '"' +
45718             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45719             ' frameborder="0" scrolling="no"></iframe>' ;
45720
45721         return html ;
45722     },
45723     
45724     _insertHtmlBefore : function( html, element )
45725     {
45726         if ( element.insertAdjacentHTML )       {
45727             // IE
45728             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45729         } else { // Gecko
45730             var oRange = document.createRange() ;
45731             oRange.setStartBefore( element ) ;
45732             var oFragment = oRange.createContextualFragment( html );
45733             element.parentNode.insertBefore( oFragment, element ) ;
45734         }
45735     }
45736     
45737     
45738   
45739     
45740     
45741     
45742     
45743
45744 });
45745
45746 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45747
45748 function FCKeditor_OnComplete(editorInstance){
45749     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45750     f.fckEditor = editorInstance;
45751     //console.log("loaded");
45752     f.fireEvent('editorinit', f, editorInstance);
45753
45754   
45755
45756  
45757
45758
45759
45760
45761
45762
45763
45764
45765
45766
45767
45768
45769
45770
45771
45772 //<script type="text/javascript">
45773 /**
45774  * @class Roo.form.GridField
45775  * @extends Roo.form.Field
45776  * Embed a grid (or editable grid into a form)
45777  * STATUS ALPHA
45778  * 
45779  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45780  * it needs 
45781  * xgrid.store = Roo.data.Store
45782  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45783  * xgrid.store.reader = Roo.data.JsonReader 
45784  * 
45785  * 
45786  * @constructor
45787  * Creates a new GridField
45788  * @param {Object} config Configuration options
45789  */
45790 Roo.form.GridField = function(config){
45791     Roo.form.GridField.superclass.constructor.call(this, config);
45792      
45793 };
45794
45795 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45796     /**
45797      * @cfg {Number} width  - used to restrict width of grid..
45798      */
45799     width : 100,
45800     /**
45801      * @cfg {Number} height - used to restrict height of grid..
45802      */
45803     height : 50,
45804      /**
45805      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45806          * 
45807          *}
45808      */
45809     xgrid : false, 
45810     /**
45811      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45812      * {tag: "input", type: "checkbox", autocomplete: "off"})
45813      */
45814    // defaultAutoCreate : { tag: 'div' },
45815     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45816     /**
45817      * @cfg {String} addTitle Text to include for adding a title.
45818      */
45819     addTitle : false,
45820     //
45821     onResize : function(){
45822         Roo.form.Field.superclass.onResize.apply(this, arguments);
45823     },
45824
45825     initEvents : function(){
45826         // Roo.form.Checkbox.superclass.initEvents.call(this);
45827         // has no events...
45828        
45829     },
45830
45831
45832     getResizeEl : function(){
45833         return this.wrap;
45834     },
45835
45836     getPositionEl : function(){
45837         return this.wrap;
45838     },
45839
45840     // private
45841     onRender : function(ct, position){
45842         
45843         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45844         var style = this.style;
45845         delete this.style;
45846         
45847         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45848         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45849         this.viewEl = this.wrap.createChild({ tag: 'div' });
45850         if (style) {
45851             this.viewEl.applyStyles(style);
45852         }
45853         if (this.width) {
45854             this.viewEl.setWidth(this.width);
45855         }
45856         if (this.height) {
45857             this.viewEl.setHeight(this.height);
45858         }
45859         //if(this.inputValue !== undefined){
45860         //this.setValue(this.value);
45861         
45862         
45863         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45864         
45865         
45866         this.grid.render();
45867         this.grid.getDataSource().on('remove', this.refreshValue, this);
45868         this.grid.getDataSource().on('update', this.refreshValue, this);
45869         this.grid.on('afteredit', this.refreshValue, this);
45870  
45871     },
45872      
45873     
45874     /**
45875      * Sets the value of the item. 
45876      * @param {String} either an object  or a string..
45877      */
45878     setValue : function(v){
45879         //this.value = v;
45880         v = v || []; // empty set..
45881         // this does not seem smart - it really only affects memoryproxy grids..
45882         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45883             var ds = this.grid.getDataSource();
45884             // assumes a json reader..
45885             var data = {}
45886             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45887             ds.loadData( data);
45888         }
45889         // clear selection so it does not get stale.
45890         if (this.grid.sm) { 
45891             this.grid.sm.clearSelections();
45892         }
45893         
45894         Roo.form.GridField.superclass.setValue.call(this, v);
45895         this.refreshValue();
45896         // should load data in the grid really....
45897     },
45898     
45899     // private
45900     refreshValue: function() {
45901          var val = [];
45902         this.grid.getDataSource().each(function(r) {
45903             val.push(r.data);
45904         });
45905         this.el.dom.value = Roo.encode(val);
45906     }
45907     
45908      
45909     
45910     
45911 });/*
45912  * Based on:
45913  * Ext JS Library 1.1.1
45914  * Copyright(c) 2006-2007, Ext JS, LLC.
45915  *
45916  * Originally Released Under LGPL - original licence link has changed is not relivant.
45917  *
45918  * Fork - LGPL
45919  * <script type="text/javascript">
45920  */
45921 /**
45922  * @class Roo.form.DisplayField
45923  * @extends Roo.form.Field
45924  * A generic Field to display non-editable data.
45925  * @constructor
45926  * Creates a new Display Field item.
45927  * @param {Object} config Configuration options
45928  */
45929 Roo.form.DisplayField = function(config){
45930     Roo.form.DisplayField.superclass.constructor.call(this, config);
45931     
45932 };
45933
45934 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45935     inputType:      'hidden',
45936     allowBlank:     true,
45937     readOnly:         true,
45938     
45939  
45940     /**
45941      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45942      */
45943     focusClass : undefined,
45944     /**
45945      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45946      */
45947     fieldClass: 'x-form-field',
45948     
45949      /**
45950      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45951      */
45952     valueRenderer: undefined,
45953     
45954     width: 100,
45955     /**
45956      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45957      * {tag: "input", type: "checkbox", autocomplete: "off"})
45958      */
45959      
45960  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45961
45962     onResize : function(){
45963         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45964         
45965     },
45966
45967     initEvents : function(){
45968         // Roo.form.Checkbox.superclass.initEvents.call(this);
45969         // has no events...
45970        
45971     },
45972
45973
45974     getResizeEl : function(){
45975         return this.wrap;
45976     },
45977
45978     getPositionEl : function(){
45979         return this.wrap;
45980     },
45981
45982     // private
45983     onRender : function(ct, position){
45984         
45985         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45986         //if(this.inputValue !== undefined){
45987         this.wrap = this.el.wrap();
45988         
45989         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45990         
45991         if (this.bodyStyle) {
45992             this.viewEl.applyStyles(this.bodyStyle);
45993         }
45994         //this.viewEl.setStyle('padding', '2px');
45995         
45996         this.setValue(this.value);
45997         
45998     },
45999 /*
46000     // private
46001     initValue : Roo.emptyFn,
46002
46003   */
46004
46005         // private
46006     onClick : function(){
46007         
46008     },
46009
46010     /**
46011      * Sets the checked state of the checkbox.
46012      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46013      */
46014     setValue : function(v){
46015         this.value = v;
46016         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46017         // this might be called before we have a dom element..
46018         if (!this.viewEl) {
46019             return;
46020         }
46021         this.viewEl.dom.innerHTML = html;
46022         Roo.form.DisplayField.superclass.setValue.call(this, v);
46023
46024     }
46025 });/*
46026  * 
46027  * Licence- LGPL
46028  * 
46029  */
46030
46031 /**
46032  * @class Roo.form.DayPicker
46033  * @extends Roo.form.Field
46034  * A Day picker show [M] [T] [W] ....
46035  * @constructor
46036  * Creates a new Day Picker
46037  * @param {Object} config Configuration options
46038  */
46039 Roo.form.DayPicker= function(config){
46040     Roo.form.DayPicker.superclass.constructor.call(this, config);
46041      
46042 };
46043
46044 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46045     /**
46046      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46047      */
46048     focusClass : undefined,
46049     /**
46050      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46051      */
46052     fieldClass: "x-form-field",
46053    
46054     /**
46055      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46056      * {tag: "input", type: "checkbox", autocomplete: "off"})
46057      */
46058     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46059     
46060    
46061     actionMode : 'viewEl', 
46062     //
46063     // private
46064  
46065     inputType : 'hidden',
46066     
46067      
46068     inputElement: false, // real input element?
46069     basedOn: false, // ????
46070     
46071     isFormField: true, // not sure where this is needed!!!!
46072
46073     onResize : function(){
46074         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46075         if(!this.boxLabel){
46076             this.el.alignTo(this.wrap, 'c-c');
46077         }
46078     },
46079
46080     initEvents : function(){
46081         Roo.form.Checkbox.superclass.initEvents.call(this);
46082         this.el.on("click", this.onClick,  this);
46083         this.el.on("change", this.onClick,  this);
46084     },
46085
46086
46087     getResizeEl : function(){
46088         return this.wrap;
46089     },
46090
46091     getPositionEl : function(){
46092         return this.wrap;
46093     },
46094
46095     
46096     // private
46097     onRender : function(ct, position){
46098         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46099        
46100         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46101         
46102         var r1 = '<table><tr>';
46103         var r2 = '<tr class="x-form-daypick-icons">';
46104         for (var i=0; i < 7; i++) {
46105             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46106             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46107         }
46108         
46109         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46110         viewEl.select('img').on('click', this.onClick, this);
46111         this.viewEl = viewEl;   
46112         
46113         
46114         // this will not work on Chrome!!!
46115         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46116         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46117         
46118         
46119           
46120
46121     },
46122
46123     // private
46124     initValue : Roo.emptyFn,
46125
46126     /**
46127      * Returns the checked state of the checkbox.
46128      * @return {Boolean} True if checked, else false
46129      */
46130     getValue : function(){
46131         return this.el.dom.value;
46132         
46133     },
46134
46135         // private
46136     onClick : function(e){ 
46137         //this.setChecked(!this.checked);
46138         Roo.get(e.target).toggleClass('x-menu-item-checked');
46139         this.refreshValue();
46140         //if(this.el.dom.checked != this.checked){
46141         //    this.setValue(this.el.dom.checked);
46142        // }
46143     },
46144     
46145     // private
46146     refreshValue : function()
46147     {
46148         var val = '';
46149         this.viewEl.select('img',true).each(function(e,i,n)  {
46150             val += e.is(".x-menu-item-checked") ? String(n) : '';
46151         });
46152         this.setValue(val, true);
46153     },
46154
46155     /**
46156      * Sets the checked state of the checkbox.
46157      * On is always based on a string comparison between inputValue and the param.
46158      * @param {Boolean/String} value - the value to set 
46159      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46160      */
46161     setValue : function(v,suppressEvent){
46162         if (!this.el.dom) {
46163             return;
46164         }
46165         var old = this.el.dom.value ;
46166         this.el.dom.value = v;
46167         if (suppressEvent) {
46168             return ;
46169         }
46170          
46171         // update display..
46172         this.viewEl.select('img',true).each(function(e,i,n)  {
46173             
46174             var on = e.is(".x-menu-item-checked");
46175             var newv = v.indexOf(String(n)) > -1;
46176             if (on != newv) {
46177                 e.toggleClass('x-menu-item-checked');
46178             }
46179             
46180         });
46181         
46182         
46183         this.fireEvent('change', this, v, old);
46184         
46185         
46186     },
46187    
46188     // handle setting of hidden value by some other method!!?!?
46189     setFromHidden: function()
46190     {
46191         if(!this.el){
46192             return;
46193         }
46194         //console.log("SET FROM HIDDEN");
46195         //alert('setFrom hidden');
46196         this.setValue(this.el.dom.value);
46197     },
46198     
46199     onDestroy : function()
46200     {
46201         if(this.viewEl){
46202             Roo.get(this.viewEl).remove();
46203         }
46204          
46205         Roo.form.DayPicker.superclass.onDestroy.call(this);
46206     }
46207
46208 });/*
46209  * RooJS Library 1.1.1
46210  * Copyright(c) 2008-2011  Alan Knowles
46211  *
46212  * License - LGPL
46213  */
46214  
46215
46216 /**
46217  * @class Roo.form.ComboCheck
46218  * @extends Roo.form.ComboBox
46219  * A combobox for multiple select items.
46220  *
46221  * FIXME - could do with a reset button..
46222  * 
46223  * @constructor
46224  * Create a new ComboCheck
46225  * @param {Object} config Configuration options
46226  */
46227 Roo.form.ComboCheck = function(config){
46228     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46229     // should verify some data...
46230     // like
46231     // hiddenName = required..
46232     // displayField = required
46233     // valudField == required
46234     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46235     var _t = this;
46236     Roo.each(req, function(e) {
46237         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46238             throw "Roo.form.ComboCheck : missing value for: " + e;
46239         }
46240     });
46241     
46242     
46243 };
46244
46245 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46246      
46247      
46248     editable : false,
46249      
46250     selectedClass: 'x-menu-item-checked', 
46251     
46252     // private
46253     onRender : function(ct, position){
46254         var _t = this;
46255         
46256         
46257         
46258         if(!this.tpl){
46259             var cls = 'x-combo-list';
46260
46261             
46262             this.tpl =  new Roo.Template({
46263                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46264                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46265                    '<span>{' + this.displayField + '}</span>' +
46266                     '</div>' 
46267                 
46268             });
46269         }
46270  
46271         
46272         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46273         this.view.singleSelect = false;
46274         this.view.multiSelect = true;
46275         this.view.toggleSelect = true;
46276         this.pageTb.add(new Roo.Toolbar.Fill(), {
46277             
46278             text: 'Done',
46279             handler: function()
46280             {
46281                 _t.collapse();
46282             }
46283         });
46284     },
46285     
46286     onViewOver : function(e, t){
46287         // do nothing...
46288         return;
46289         
46290     },
46291     
46292     onViewClick : function(doFocus,index){
46293         return;
46294         
46295     },
46296     select: function () {
46297         //Roo.log("SELECT CALLED");
46298     },
46299      
46300     selectByValue : function(xv, scrollIntoView){
46301         var ar = this.getValueArray();
46302         var sels = [];
46303         
46304         Roo.each(ar, function(v) {
46305             if(v === undefined || v === null){
46306                 return;
46307             }
46308             var r = this.findRecord(this.valueField, v);
46309             if(r){
46310                 sels.push(this.store.indexOf(r))
46311                 
46312             }
46313         },this);
46314         this.view.select(sels);
46315         return false;
46316     },
46317     
46318     
46319     
46320     onSelect : function(record, index){
46321        // Roo.log("onselect Called");
46322        // this is only called by the clear button now..
46323         this.view.clearSelections();
46324         this.setValue('[]');
46325         if (this.value != this.valueBefore) {
46326             this.fireEvent('change', this, this.value, this.valueBefore);
46327             this.valueBefore = this.value;
46328         }
46329     },
46330     getValueArray : function()
46331     {
46332         var ar = [] ;
46333         
46334         try {
46335             //Roo.log(this.value);
46336             if (typeof(this.value) == 'undefined') {
46337                 return [];
46338             }
46339             var ar = Roo.decode(this.value);
46340             return  ar instanceof Array ? ar : []; //?? valid?
46341             
46342         } catch(e) {
46343             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46344             return [];
46345         }
46346          
46347     },
46348     expand : function ()
46349     {
46350         
46351         Roo.form.ComboCheck.superclass.expand.call(this);
46352         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46353         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46354         
46355
46356     },
46357     
46358     collapse : function(){
46359         Roo.form.ComboCheck.superclass.collapse.call(this);
46360         var sl = this.view.getSelectedIndexes();
46361         var st = this.store;
46362         var nv = [];
46363         var tv = [];
46364         var r;
46365         Roo.each(sl, function(i) {
46366             r = st.getAt(i);
46367             nv.push(r.get(this.valueField));
46368         },this);
46369         this.setValue(Roo.encode(nv));
46370         if (this.value != this.valueBefore) {
46371
46372             this.fireEvent('change', this, this.value, this.valueBefore);
46373             this.valueBefore = this.value;
46374         }
46375         
46376     },
46377     
46378     setValue : function(v){
46379         // Roo.log(v);
46380         this.value = v;
46381         
46382         var vals = this.getValueArray();
46383         var tv = [];
46384         Roo.each(vals, function(k) {
46385             var r = this.findRecord(this.valueField, k);
46386             if(r){
46387                 tv.push(r.data[this.displayField]);
46388             }else if(this.valueNotFoundText !== undefined){
46389                 tv.push( this.valueNotFoundText );
46390             }
46391         },this);
46392        // Roo.log(tv);
46393         
46394         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46395         this.hiddenField.value = v;
46396         this.value = v;
46397     }
46398     
46399 });/*
46400  * Based on:
46401  * Ext JS Library 1.1.1
46402  * Copyright(c) 2006-2007, Ext JS, LLC.
46403  *
46404  * Originally Released Under LGPL - original licence link has changed is not relivant.
46405  *
46406  * Fork - LGPL
46407  * <script type="text/javascript">
46408  */
46409  
46410 /**
46411  * @class Roo.form.Signature
46412  * @extends Roo.form.Field
46413  * Signature field.  
46414  * @constructor
46415  * 
46416  * @param {Object} config Configuration options
46417  */
46418
46419 Roo.form.Signature = function(config){
46420     Roo.form.Signature.superclass.constructor.call(this, config);
46421     
46422     this.addEvents({// not in used??
46423          /**
46424          * @event confirm
46425          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46426              * @param {Roo.form.Signature} combo This combo box
46427              */
46428         'confirm' : true,
46429         /**
46430          * @event reset
46431          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46432              * @param {Roo.form.ComboBox} combo This combo box
46433              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46434              */
46435         'reset' : true
46436     });
46437 };
46438
46439 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46440     /**
46441      * @cfg {Object} labels Label to use when rendering a form.
46442      * defaults to 
46443      * labels : { 
46444      *      clear : "Clear",
46445      *      confirm : "Confirm"
46446      *  }
46447      */
46448     labels : { 
46449         clear : "Clear",
46450         confirm : "Confirm"
46451     },
46452     /**
46453      * @cfg {Number} width The signature panel width (defaults to 300)
46454      */
46455     width: 300,
46456     /**
46457      * @cfg {Number} height The signature panel height (defaults to 100)
46458      */
46459     height : 100,
46460     /**
46461      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46462      */
46463     allowBlank : false,
46464     
46465     //private
46466     // {Object} signPanel The signature SVG panel element (defaults to {})
46467     signPanel : {},
46468     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46469     isMouseDown : false,
46470     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46471     isConfirmed : false,
46472     // {String} signatureTmp SVG mapping string (defaults to empty string)
46473     signatureTmp : '',
46474     
46475     
46476     defaultAutoCreate : { // modified by initCompnoent..
46477         tag: "input",
46478         type:"hidden"
46479     },
46480
46481     // private
46482     onRender : function(ct, position){
46483         
46484         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46485         
46486         this.wrap = this.el.wrap({
46487             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46488         });
46489         
46490         this.createToolbar(this);
46491         this.signPanel = this.wrap.createChild({
46492                 tag: 'div',
46493                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46494             }, this.el
46495         );
46496             
46497         this.svgID = Roo.id();
46498         this.svgEl = this.signPanel.createChild({
46499               xmlns : 'http://www.w3.org/2000/svg',
46500               tag : 'svg',
46501               id : this.svgID + "-svg",
46502               width: this.width,
46503               height: this.height,
46504               viewBox: '0 0 '+this.width+' '+this.height,
46505               cn : [
46506                 {
46507                     tag: "rect",
46508                     id: this.svgID + "-svg-r",
46509                     width: this.width,
46510                     height: this.height,
46511                     fill: "#ffa"
46512                 },
46513                 {
46514                     tag: "line",
46515                     id: this.svgID + "-svg-l",
46516                     x1: "0", // start
46517                     y1: (this.height*0.8), // start set the line in 80% of height
46518                     x2: this.width, // end
46519                     y2: (this.height*0.8), // end set the line in 80% of height
46520                     'stroke': "#666",
46521                     'stroke-width': "1",
46522                     'stroke-dasharray': "3",
46523                     'shape-rendering': "crispEdges",
46524                     'pointer-events': "none"
46525                 },
46526                 {
46527                     tag: "path",
46528                     id: this.svgID + "-svg-p",
46529                     'stroke': "navy",
46530                     'stroke-width': "3",
46531                     'fill': "none",
46532                     'pointer-events': 'none'
46533                 }
46534               ]
46535         });
46536         this.createSVG();
46537         this.svgBox = this.svgEl.dom.getScreenCTM();
46538     },
46539     createSVG : function(){ 
46540         var svg = this.signPanel;
46541         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46542         var t = this;
46543
46544         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46545         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46546         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46547         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46548         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46549         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46550         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46551         
46552     },
46553     isTouchEvent : function(e){
46554         return e.type.match(/^touch/);
46555     },
46556     getCoords : function (e) {
46557         var pt    = this.svgEl.dom.createSVGPoint();
46558         pt.x = e.clientX; 
46559         pt.y = e.clientY;
46560         if (this.isTouchEvent(e)) {
46561             pt.x =  e.targetTouches[0].clientX 
46562             pt.y = e.targetTouches[0].clientY;
46563         }
46564         var a = this.svgEl.dom.getScreenCTM();
46565         var b = a.inverse();
46566         var mx = pt.matrixTransform(b);
46567         return mx.x + ',' + mx.y;
46568     },
46569     //mouse event headler 
46570     down : function (e) {
46571         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46572         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46573         
46574         this.isMouseDown = true;
46575         
46576         e.preventDefault();
46577     },
46578     move : function (e) {
46579         if (this.isMouseDown) {
46580             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46581             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46582         }
46583         
46584         e.preventDefault();
46585     },
46586     up : function (e) {
46587         this.isMouseDown = false;
46588         var sp = this.signatureTmp.split(' ');
46589         
46590         if(sp.length > 1){
46591             if(!sp[sp.length-2].match(/^L/)){
46592                 sp.pop();
46593                 sp.pop();
46594                 sp.push("");
46595                 this.signatureTmp = sp.join(" ");
46596             }
46597         }
46598         if(this.getValue() != this.signatureTmp){
46599             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46600             this.isConfirmed = false;
46601         }
46602         e.preventDefault();
46603     },
46604     
46605     /**
46606      * Protected method that will not generally be called directly. It
46607      * is called when the editor creates its toolbar. Override this method if you need to
46608      * add custom toolbar buttons.
46609      * @param {HtmlEditor} editor
46610      */
46611     createToolbar : function(editor){
46612          function btn(id, toggle, handler){
46613             var xid = fid + '-'+ id ;
46614             return {
46615                 id : xid,
46616                 cmd : id,
46617                 cls : 'x-btn-icon x-edit-'+id,
46618                 enableToggle:toggle !== false,
46619                 scope: editor, // was editor...
46620                 handler:handler||editor.relayBtnCmd,
46621                 clickEvent:'mousedown',
46622                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46623                 tabIndex:-1
46624             };
46625         }
46626         
46627         
46628         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46629         this.tb = tb;
46630         this.tb.add(
46631            {
46632                 cls : ' x-signature-btn x-signature-'+id,
46633                 scope: editor, // was editor...
46634                 handler: this.reset,
46635                 clickEvent:'mousedown',
46636                 text: this.labels.clear
46637             },
46638             {
46639                  xtype : 'Fill',
46640                  xns: Roo.Toolbar
46641             }, 
46642             {
46643                 cls : '  x-signature-btn x-signature-'+id,
46644                 scope: editor, // was editor...
46645                 handler: this.confirmHandler,
46646                 clickEvent:'mousedown',
46647                 text: this.labels.confirm
46648             }
46649         );
46650     
46651     },
46652     //public
46653     /**
46654      * when user is clicked confirm then show this image.....
46655      * 
46656      * @return {String} Image Data URI
46657      */
46658     getImageDataURI : function(){
46659         var svg = this.svgEl.dom.parentNode.innerHTML;
46660         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46661         return src; 
46662     },
46663     /**
46664      * 
46665      * @return {Boolean} this.isConfirmed
46666      */
46667     getConfirmed : function(){
46668         return this.isConfirmed;
46669     },
46670     /**
46671      * 
46672      * @return {Number} this.width
46673      */
46674     getWidth : function(){
46675         return this.width;
46676     },
46677     /**
46678      * 
46679      * @return {Number} this.height
46680      */
46681     getHeight : function(){
46682         return this.height;
46683     },
46684     // private
46685     getSignature : function(){
46686         return this.signatureTmp;
46687     },
46688     // private
46689     reset : function(){
46690         this.signatureTmp = '';
46691         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46692         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46693         this.isConfirmed = false;
46694         Roo.form.Signature.superclass.reset.call(this);
46695     },
46696     setSignature : function(s){
46697         this.signatureTmp = s;
46698         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46699         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46700         this.setValue(s);
46701         this.isConfirmed = false;
46702         Roo.form.Signature.superclass.reset.call(this);
46703     }, 
46704     test : function(){
46705 //        Roo.log(this.signPanel.dom.contentWindow.up())
46706     },
46707     //private
46708     setConfirmed : function(){
46709         
46710         
46711         
46712 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46713     },
46714     // private
46715     confirmHandler : function(){
46716         if(!this.getSignature()){
46717             return;
46718         }
46719         
46720         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46721         this.setValue(this.getSignature());
46722         this.isConfirmed = true;
46723         
46724         this.fireEvent('confirm', this);
46725     },
46726     // private
46727     // Subclasses should provide the validation implementation by overriding this
46728     validateValue : function(value){
46729         if(this.allowBlank){
46730             return true;
46731         }
46732         
46733         if(this.isConfirmed){
46734             return true;
46735         }
46736         return false;
46737     }
46738 });/*
46739  * Based on:
46740  * Ext JS Library 1.1.1
46741  * Copyright(c) 2006-2007, Ext JS, LLC.
46742  *
46743  * Originally Released Under LGPL - original licence link has changed is not relivant.
46744  *
46745  * Fork - LGPL
46746  * <script type="text/javascript">
46747  */
46748  
46749
46750 /**
46751  * @class Roo.form.ComboBox
46752  * @extends Roo.form.TriggerField
46753  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46754  * @constructor
46755  * Create a new ComboBox.
46756  * @param {Object} config Configuration options
46757  */
46758 Roo.form.Select = function(config){
46759     Roo.form.Select.superclass.constructor.call(this, config);
46760      
46761 };
46762
46763 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46764     /**
46765      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46766      */
46767     /**
46768      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46769      * rendering into an Roo.Editor, defaults to false)
46770      */
46771     /**
46772      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46773      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46774      */
46775     /**
46776      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46777      */
46778     /**
46779      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46780      * the dropdown list (defaults to undefined, with no header element)
46781      */
46782
46783      /**
46784      * @cfg {String/Roo.Template} tpl The template to use to render the output
46785      */
46786      
46787     // private
46788     defaultAutoCreate : {tag: "select"  },
46789     /**
46790      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46791      */
46792     listWidth: undefined,
46793     /**
46794      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46795      * mode = 'remote' or 'text' if mode = 'local')
46796      */
46797     displayField: undefined,
46798     /**
46799      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46800      * mode = 'remote' or 'value' if mode = 'local'). 
46801      * Note: use of a valueField requires the user make a selection
46802      * in order for a value to be mapped.
46803      */
46804     valueField: undefined,
46805     
46806     
46807     /**
46808      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46809      * field's data value (defaults to the underlying DOM element's name)
46810      */
46811     hiddenName: undefined,
46812     /**
46813      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46814      */
46815     listClass: '',
46816     /**
46817      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46818      */
46819     selectedClass: 'x-combo-selected',
46820     /**
46821      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46822      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46823      * which displays a downward arrow icon).
46824      */
46825     triggerClass : 'x-form-arrow-trigger',
46826     /**
46827      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46828      */
46829     shadow:'sides',
46830     /**
46831      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46832      * anchor positions (defaults to 'tl-bl')
46833      */
46834     listAlign: 'tl-bl?',
46835     /**
46836      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46837      */
46838     maxHeight: 300,
46839     /**
46840      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46841      * query specified by the allQuery config option (defaults to 'query')
46842      */
46843     triggerAction: 'query',
46844     /**
46845      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46846      * (defaults to 4, does not apply if editable = false)
46847      */
46848     minChars : 4,
46849     /**
46850      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46851      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46852      */
46853     typeAhead: false,
46854     /**
46855      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46856      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46857      */
46858     queryDelay: 500,
46859     /**
46860      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46861      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46862      */
46863     pageSize: 0,
46864     /**
46865      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46866      * when editable = true (defaults to false)
46867      */
46868     selectOnFocus:false,
46869     /**
46870      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46871      */
46872     queryParam: 'query',
46873     /**
46874      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46875      * when mode = 'remote' (defaults to 'Loading...')
46876      */
46877     loadingText: 'Loading...',
46878     /**
46879      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46880      */
46881     resizable: false,
46882     /**
46883      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46884      */
46885     handleHeight : 8,
46886     /**
46887      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46888      * traditional select (defaults to true)
46889      */
46890     editable: true,
46891     /**
46892      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46893      */
46894     allQuery: '',
46895     /**
46896      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46897      */
46898     mode: 'remote',
46899     /**
46900      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46901      * listWidth has a higher value)
46902      */
46903     minListWidth : 70,
46904     /**
46905      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46906      * allow the user to set arbitrary text into the field (defaults to false)
46907      */
46908     forceSelection:false,
46909     /**
46910      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46911      * if typeAhead = true (defaults to 250)
46912      */
46913     typeAheadDelay : 250,
46914     /**
46915      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46916      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46917      */
46918     valueNotFoundText : undefined,
46919     
46920     /**
46921      * @cfg {String} defaultValue The value displayed after loading the store.
46922      */
46923     defaultValue: '',
46924     
46925     /**
46926      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46927      */
46928     blockFocus : false,
46929     
46930     /**
46931      * @cfg {Boolean} disableClear Disable showing of clear button.
46932      */
46933     disableClear : false,
46934     /**
46935      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46936      */
46937     alwaysQuery : false,
46938     
46939     //private
46940     addicon : false,
46941     editicon: false,
46942     
46943     // element that contains real text value.. (when hidden is used..)
46944      
46945     // private
46946     onRender : function(ct, position){
46947         Roo.form.Field.prototype.onRender.call(this, ct, position);
46948         
46949         if(this.store){
46950             this.store.on('beforeload', this.onBeforeLoad, this);
46951             this.store.on('load', this.onLoad, this);
46952             this.store.on('loadexception', this.onLoadException, this);
46953             this.store.load({});
46954         }
46955         
46956         
46957         
46958     },
46959
46960     // private
46961     initEvents : function(){
46962         //Roo.form.ComboBox.superclass.initEvents.call(this);
46963  
46964     },
46965
46966     onDestroy : function(){
46967        
46968         if(this.store){
46969             this.store.un('beforeload', this.onBeforeLoad, this);
46970             this.store.un('load', this.onLoad, this);
46971             this.store.un('loadexception', this.onLoadException, this);
46972         }
46973         //Roo.form.ComboBox.superclass.onDestroy.call(this);
46974     },
46975
46976     // private
46977     fireKey : function(e){
46978         if(e.isNavKeyPress() && !this.list.isVisible()){
46979             this.fireEvent("specialkey", this, e);
46980         }
46981     },
46982
46983     // private
46984     onResize: function(w, h){
46985         
46986         return; 
46987     
46988         
46989     },
46990
46991     /**
46992      * Allow or prevent the user from directly editing the field text.  If false is passed,
46993      * the user will only be able to select from the items defined in the dropdown list.  This method
46994      * is the runtime equivalent of setting the 'editable' config option at config time.
46995      * @param {Boolean} value True to allow the user to directly edit the field text
46996      */
46997     setEditable : function(value){
46998          
46999     },
47000
47001     // private
47002     onBeforeLoad : function(){
47003         
47004         Roo.log("Select before load");
47005         return;
47006     
47007         this.innerList.update(this.loadingText ?
47008                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47009         //this.restrictHeight();
47010         this.selectedIndex = -1;
47011     },
47012
47013     // private
47014     onLoad : function(){
47015
47016     
47017         var dom = this.el.dom;
47018         dom.innerHTML = '';
47019          var od = dom.ownerDocument;
47020          
47021         if (this.emptyText) {
47022             var op = od.createElement('option');
47023             op.setAttribute('value', '');
47024             op.innerHTML = String.format('{0}', this.emptyText);
47025             dom.appendChild(op);
47026         }
47027         if(this.store.getCount() > 0){
47028            
47029             var vf = this.valueField;
47030             var df = this.displayField;
47031             this.store.data.each(function(r) {
47032                 // which colmsn to use... testing - cdoe / title..
47033                 var op = od.createElement('option');
47034                 op.setAttribute('value', r.data[vf]);
47035                 op.innerHTML = String.format('{0}', r.data[df]);
47036                 dom.appendChild(op);
47037             });
47038             if (typeof(this.defaultValue != 'undefined')) {
47039                 this.setValue(this.defaultValue);
47040             }
47041             
47042              
47043         }else{
47044             //this.onEmptyResults();
47045         }
47046         //this.el.focus();
47047     },
47048     // private
47049     onLoadException : function()
47050     {
47051         dom.innerHTML = '';
47052             
47053         Roo.log("Select on load exception");
47054         return;
47055     
47056         this.collapse();
47057         Roo.log(this.store.reader.jsonData);
47058         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47059             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47060         }
47061         
47062         
47063     },
47064     // private
47065     onTypeAhead : function(){
47066          
47067     },
47068
47069     // private
47070     onSelect : function(record, index){
47071         Roo.log('on select?');
47072         return;
47073         if(this.fireEvent('beforeselect', this, record, index) !== false){
47074             this.setFromData(index > -1 ? record.data : false);
47075             this.collapse();
47076             this.fireEvent('select', this, record, index);
47077         }
47078     },
47079
47080     /**
47081      * Returns the currently selected field value or empty string if no value is set.
47082      * @return {String} value The selected value
47083      */
47084     getValue : function(){
47085         var dom = this.el.dom;
47086         this.value = dom.options[dom.selectedIndex].value;
47087         return this.value;
47088         
47089     },
47090
47091     /**
47092      * Clears any text/value currently set in the field
47093      */
47094     clearValue : function(){
47095         this.value = '';
47096         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47097         
47098     },
47099
47100     /**
47101      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47102      * will be displayed in the field.  If the value does not match the data value of an existing item,
47103      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47104      * Otherwise the field will be blank (although the value will still be set).
47105      * @param {String} value The value to match
47106      */
47107     setValue : function(v){
47108         var d = this.el.dom;
47109         for (var i =0; i < d.options.length;i++) {
47110             if (v == d.options[i].value) {
47111                 d.selectedIndex = i;
47112                 this.value = v;
47113                 return;
47114             }
47115         }
47116         this.clearValue();
47117     },
47118     /**
47119      * @property {Object} the last set data for the element
47120      */
47121     
47122     lastData : false,
47123     /**
47124      * Sets the value of the field based on a object which is related to the record format for the store.
47125      * @param {Object} value the value to set as. or false on reset?
47126      */
47127     setFromData : function(o){
47128         Roo.log('setfrom data?');
47129          
47130         
47131         
47132     },
47133     // private
47134     reset : function(){
47135         this.clearValue();
47136     },
47137     // private
47138     findRecord : function(prop, value){
47139         
47140         return false;
47141     
47142         var record;
47143         if(this.store.getCount() > 0){
47144             this.store.each(function(r){
47145                 if(r.data[prop] == value){
47146                     record = r;
47147                     return false;
47148                 }
47149                 return true;
47150             });
47151         }
47152         return record;
47153     },
47154     
47155     getName: function()
47156     {
47157         // returns hidden if it's set..
47158         if (!this.rendered) {return ''};
47159         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47160         
47161     },
47162      
47163
47164     
47165
47166     // private
47167     onEmptyResults : function(){
47168         Roo.log('empty results');
47169         //this.collapse();
47170     },
47171
47172     /**
47173      * Returns true if the dropdown list is expanded, else false.
47174      */
47175     isExpanded : function(){
47176         return false;
47177     },
47178
47179     /**
47180      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47181      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47182      * @param {String} value The data value of the item to select
47183      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47184      * selected item if it is not currently in view (defaults to true)
47185      * @return {Boolean} True if the value matched an item in the list, else false
47186      */
47187     selectByValue : function(v, scrollIntoView){
47188         Roo.log('select By Value');
47189         return false;
47190     
47191         if(v !== undefined && v !== null){
47192             var r = this.findRecord(this.valueField || this.displayField, v);
47193             if(r){
47194                 this.select(this.store.indexOf(r), scrollIntoView);
47195                 return true;
47196             }
47197         }
47198         return false;
47199     },
47200
47201     /**
47202      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47203      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47204      * @param {Number} index The zero-based index of the list item to select
47205      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47206      * selected item if it is not currently in view (defaults to true)
47207      */
47208     select : function(index, scrollIntoView){
47209         Roo.log('select ');
47210         return  ;
47211         
47212         this.selectedIndex = index;
47213         this.view.select(index);
47214         if(scrollIntoView !== false){
47215             var el = this.view.getNode(index);
47216             if(el){
47217                 this.innerList.scrollChildIntoView(el, false);
47218             }
47219         }
47220     },
47221
47222       
47223
47224     // private
47225     validateBlur : function(){
47226         
47227         return;
47228         
47229     },
47230
47231     // private
47232     initQuery : function(){
47233         this.doQuery(this.getRawValue());
47234     },
47235
47236     // private
47237     doForce : function(){
47238         if(this.el.dom.value.length > 0){
47239             this.el.dom.value =
47240                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47241              
47242         }
47243     },
47244
47245     /**
47246      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47247      * query allowing the query action to be canceled if needed.
47248      * @param {String} query The SQL query to execute
47249      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47250      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47251      * saved in the current store (defaults to false)
47252      */
47253     doQuery : function(q, forceAll){
47254         
47255         Roo.log('doQuery?');
47256         if(q === undefined || q === null){
47257             q = '';
47258         }
47259         var qe = {
47260             query: q,
47261             forceAll: forceAll,
47262             combo: this,
47263             cancel:false
47264         };
47265         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47266             return false;
47267         }
47268         q = qe.query;
47269         forceAll = qe.forceAll;
47270         if(forceAll === true || (q.length >= this.minChars)){
47271             if(this.lastQuery != q || this.alwaysQuery){
47272                 this.lastQuery = q;
47273                 if(this.mode == 'local'){
47274                     this.selectedIndex = -1;
47275                     if(forceAll){
47276                         this.store.clearFilter();
47277                     }else{
47278                         this.store.filter(this.displayField, q);
47279                     }
47280                     this.onLoad();
47281                 }else{
47282                     this.store.baseParams[this.queryParam] = q;
47283                     this.store.load({
47284                         params: this.getParams(q)
47285                     });
47286                     this.expand();
47287                 }
47288             }else{
47289                 this.selectedIndex = -1;
47290                 this.onLoad();   
47291             }
47292         }
47293     },
47294
47295     // private
47296     getParams : function(q){
47297         var p = {};
47298         //p[this.queryParam] = q;
47299         if(this.pageSize){
47300             p.start = 0;
47301             p.limit = this.pageSize;
47302         }
47303         return p;
47304     },
47305
47306     /**
47307      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47308      */
47309     collapse : function(){
47310         
47311     },
47312
47313     // private
47314     collapseIf : function(e){
47315         
47316     },
47317
47318     /**
47319      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47320      */
47321     expand : function(){
47322         
47323     } ,
47324
47325     // private
47326      
47327
47328     /** 
47329     * @cfg {Boolean} grow 
47330     * @hide 
47331     */
47332     /** 
47333     * @cfg {Number} growMin 
47334     * @hide 
47335     */
47336     /** 
47337     * @cfg {Number} growMax 
47338     * @hide 
47339     */
47340     /**
47341      * @hide
47342      * @method autoSize
47343      */
47344     
47345     setWidth : function()
47346     {
47347         
47348     },
47349     getResizeEl : function(){
47350         return this.el;
47351     }
47352 });//<script type="text/javasscript">
47353  
47354
47355 /**
47356  * @class Roo.DDView
47357  * A DnD enabled version of Roo.View.
47358  * @param {Element/String} container The Element in which to create the View.
47359  * @param {String} tpl The template string used to create the markup for each element of the View
47360  * @param {Object} config The configuration properties. These include all the config options of
47361  * {@link Roo.View} plus some specific to this class.<br>
47362  * <p>
47363  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47364  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47365  * <p>
47366  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47367 .x-view-drag-insert-above {
47368         border-top:1px dotted #3366cc;
47369 }
47370 .x-view-drag-insert-below {
47371         border-bottom:1px dotted #3366cc;
47372 }
47373 </code></pre>
47374  * 
47375  */
47376  
47377 Roo.DDView = function(container, tpl, config) {
47378     Roo.DDView.superclass.constructor.apply(this, arguments);
47379     this.getEl().setStyle("outline", "0px none");
47380     this.getEl().unselectable();
47381     if (this.dragGroup) {
47382                 this.setDraggable(this.dragGroup.split(","));
47383     }
47384     if (this.dropGroup) {
47385                 this.setDroppable(this.dropGroup.split(","));
47386     }
47387     if (this.deletable) {
47388         this.setDeletable();
47389     }
47390     this.isDirtyFlag = false;
47391         this.addEvents({
47392                 "drop" : true
47393         });
47394 };
47395
47396 Roo.extend(Roo.DDView, Roo.View, {
47397 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47398 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47399 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47400 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47401
47402         isFormField: true,
47403
47404         reset: Roo.emptyFn,
47405         
47406         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47407
47408         validate: function() {
47409                 return true;
47410         },
47411         
47412         destroy: function() {
47413                 this.purgeListeners();
47414                 this.getEl.removeAllListeners();
47415                 this.getEl().remove();
47416                 if (this.dragZone) {
47417                         if (this.dragZone.destroy) {
47418                                 this.dragZone.destroy();
47419                         }
47420                 }
47421                 if (this.dropZone) {
47422                         if (this.dropZone.destroy) {
47423                                 this.dropZone.destroy();
47424                         }
47425                 }
47426         },
47427
47428 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47429         getName: function() {
47430                 return this.name;
47431         },
47432
47433 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47434         setValue: function(v) {
47435                 if (!this.store) {
47436                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47437                 }
47438                 var data = {};
47439                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47440                 this.store.proxy = new Roo.data.MemoryProxy(data);
47441                 this.store.load();
47442         },
47443
47444 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47445         getValue: function() {
47446                 var result = '(';
47447                 this.store.each(function(rec) {
47448                         result += rec.id + ',';
47449                 });
47450                 return result.substr(0, result.length - 1) + ')';
47451         },
47452         
47453         getIds: function() {
47454                 var i = 0, result = new Array(this.store.getCount());
47455                 this.store.each(function(rec) {
47456                         result[i++] = rec.id;
47457                 });
47458                 return result;
47459         },
47460         
47461         isDirty: function() {
47462                 return this.isDirtyFlag;
47463         },
47464
47465 /**
47466  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47467  *      whole Element becomes the target, and this causes the drop gesture to append.
47468  */
47469     getTargetFromEvent : function(e) {
47470                 var target = e.getTarget();
47471                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47472                 target = target.parentNode;
47473                 }
47474                 if (!target) {
47475                         target = this.el.dom.lastChild || this.el.dom;
47476                 }
47477                 return target;
47478     },
47479
47480 /**
47481  *      Create the drag data which consists of an object which has the property "ddel" as
47482  *      the drag proxy element. 
47483  */
47484     getDragData : function(e) {
47485         var target = this.findItemFromChild(e.getTarget());
47486                 if(target) {
47487                         this.handleSelection(e);
47488                         var selNodes = this.getSelectedNodes();
47489             var dragData = {
47490                 source: this,
47491                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47492                 nodes: selNodes,
47493                 records: []
47494                         };
47495                         var selectedIndices = this.getSelectedIndexes();
47496                         for (var i = 0; i < selectedIndices.length; i++) {
47497                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47498                         }
47499                         if (selNodes.length == 1) {
47500                                 dragData.ddel = target.cloneNode(true); // the div element
47501                         } else {
47502                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47503                                 div.className = 'multi-proxy';
47504                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47505                                         div.appendChild(selNodes[i].cloneNode(true));
47506                                 }
47507                                 dragData.ddel = div;
47508                         }
47509             //console.log(dragData)
47510             //console.log(dragData.ddel.innerHTML)
47511                         return dragData;
47512                 }
47513         //console.log('nodragData')
47514                 return false;
47515     },
47516     
47517 /**     Specify to which ddGroup items in this DDView may be dragged. */
47518     setDraggable: function(ddGroup) {
47519         if (ddGroup instanceof Array) {
47520                 Roo.each(ddGroup, this.setDraggable, this);
47521                 return;
47522         }
47523         if (this.dragZone) {
47524                 this.dragZone.addToGroup(ddGroup);
47525         } else {
47526                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47527                                 containerScroll: true,
47528                                 ddGroup: ddGroup 
47529
47530                         });
47531 //                      Draggability implies selection. DragZone's mousedown selects the element.
47532                         if (!this.multiSelect) { this.singleSelect = true; }
47533
47534 //                      Wire the DragZone's handlers up to methods in *this*
47535                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47536                 }
47537     },
47538
47539 /**     Specify from which ddGroup this DDView accepts drops. */
47540     setDroppable: function(ddGroup) {
47541         if (ddGroup instanceof Array) {
47542                 Roo.each(ddGroup, this.setDroppable, this);
47543                 return;
47544         }
47545         if (this.dropZone) {
47546                 this.dropZone.addToGroup(ddGroup);
47547         } else {
47548                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47549                                 containerScroll: true,
47550                                 ddGroup: ddGroup
47551                         });
47552
47553 //                      Wire the DropZone's handlers up to methods in *this*
47554                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47555                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47556                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47557                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47558                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47559                 }
47560     },
47561
47562 /**     Decide whether to drop above or below a View node. */
47563     getDropPoint : function(e, n, dd){
47564         if (n == this.el.dom) { return "above"; }
47565                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47566                 var c = t + (b - t) / 2;
47567                 var y = Roo.lib.Event.getPageY(e);
47568                 if(y <= c) {
47569                         return "above";
47570                 }else{
47571                         return "below";
47572                 }
47573     },
47574
47575     onNodeEnter : function(n, dd, e, data){
47576                 return false;
47577     },
47578     
47579     onNodeOver : function(n, dd, e, data){
47580                 var pt = this.getDropPoint(e, n, dd);
47581                 // set the insert point style on the target node
47582                 var dragElClass = this.dropNotAllowed;
47583                 if (pt) {
47584                         var targetElClass;
47585                         if (pt == "above"){
47586                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47587                                 targetElClass = "x-view-drag-insert-above";
47588                         } else {
47589                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47590                                 targetElClass = "x-view-drag-insert-below";
47591                         }
47592                         if (this.lastInsertClass != targetElClass){
47593                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47594                                 this.lastInsertClass = targetElClass;
47595                         }
47596                 }
47597                 return dragElClass;
47598         },
47599
47600     onNodeOut : function(n, dd, e, data){
47601                 this.removeDropIndicators(n);
47602     },
47603
47604     onNodeDrop : function(n, dd, e, data){
47605         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47606                 return false;
47607         }
47608         var pt = this.getDropPoint(e, n, dd);
47609                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47610                 if (pt == "below") { insertAt++; }
47611                 for (var i = 0; i < data.records.length; i++) {
47612                         var r = data.records[i];
47613                         var dup = this.store.getById(r.id);
47614                         if (dup && (dd != this.dragZone)) {
47615                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47616                         } else {
47617                                 if (data.copy) {
47618                                         this.store.insert(insertAt++, r.copy());
47619                                 } else {
47620                                         data.source.isDirtyFlag = true;
47621                                         r.store.remove(r);
47622                                         this.store.insert(insertAt++, r);
47623                                 }
47624                                 this.isDirtyFlag = true;
47625                         }
47626                 }
47627                 this.dragZone.cachedTarget = null;
47628                 return true;
47629     },
47630
47631     removeDropIndicators : function(n){
47632                 if(n){
47633                         Roo.fly(n).removeClass([
47634                                 "x-view-drag-insert-above",
47635                                 "x-view-drag-insert-below"]);
47636                         this.lastInsertClass = "_noclass";
47637                 }
47638     },
47639
47640 /**
47641  *      Utility method. Add a delete option to the DDView's context menu.
47642  *      @param {String} imageUrl The URL of the "delete" icon image.
47643  */
47644         setDeletable: function(imageUrl) {
47645                 if (!this.singleSelect && !this.multiSelect) {
47646                         this.singleSelect = true;
47647                 }
47648                 var c = this.getContextMenu();
47649                 this.contextMenu.on("itemclick", function(item) {
47650                         switch (item.id) {
47651                                 case "delete":
47652                                         this.remove(this.getSelectedIndexes());
47653                                         break;
47654                         }
47655                 }, this);
47656                 this.contextMenu.add({
47657                         icon: imageUrl,
47658                         id: "delete",
47659                         text: 'Delete'
47660                 });
47661         },
47662         
47663 /**     Return the context menu for this DDView. */
47664         getContextMenu: function() {
47665                 if (!this.contextMenu) {
47666 //                      Create the View's context menu
47667                         this.contextMenu = new Roo.menu.Menu({
47668                                 id: this.id + "-contextmenu"
47669                         });
47670                         this.el.on("contextmenu", this.showContextMenu, this);
47671                 }
47672                 return this.contextMenu;
47673         },
47674         
47675         disableContextMenu: function() {
47676                 if (this.contextMenu) {
47677                         this.el.un("contextmenu", this.showContextMenu, this);
47678                 }
47679         },
47680
47681         showContextMenu: function(e, item) {
47682         item = this.findItemFromChild(e.getTarget());
47683                 if (item) {
47684                         e.stopEvent();
47685                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47686                         this.contextMenu.showAt(e.getXY());
47687             }
47688     },
47689
47690 /**
47691  *      Remove {@link Roo.data.Record}s at the specified indices.
47692  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47693  */
47694     remove: function(selectedIndices) {
47695                 selectedIndices = [].concat(selectedIndices);
47696                 for (var i = 0; i < selectedIndices.length; i++) {
47697                         var rec = this.store.getAt(selectedIndices[i]);
47698                         this.store.remove(rec);
47699                 }
47700     },
47701
47702 /**
47703  *      Double click fires the event, but also, if this is draggable, and there is only one other
47704  *      related DropZone, it transfers the selected node.
47705  */
47706     onDblClick : function(e){
47707         var item = this.findItemFromChild(e.getTarget());
47708         if(item){
47709             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47710                 return false;
47711             }
47712             if (this.dragGroup) {
47713                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47714                     while (targets.indexOf(this.dropZone) > -1) {
47715                             targets.remove(this.dropZone);
47716                                 }
47717                     if (targets.length == 1) {
47718                                         this.dragZone.cachedTarget = null;
47719                         var el = Roo.get(targets[0].getEl());
47720                         var box = el.getBox(true);
47721                         targets[0].onNodeDrop(el.dom, {
47722                                 target: el.dom,
47723                                 xy: [box.x, box.y + box.height - 1]
47724                         }, null, this.getDragData(e));
47725                     }
47726                 }
47727         }
47728     },
47729     
47730     handleSelection: function(e) {
47731                 this.dragZone.cachedTarget = null;
47732         var item = this.findItemFromChild(e.getTarget());
47733         if (!item) {
47734                 this.clearSelections(true);
47735                 return;
47736         }
47737                 if (item && (this.multiSelect || this.singleSelect)){
47738                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47739                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47740                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47741                                 this.unselect(item);
47742                         } else {
47743                                 this.select(item, this.multiSelect && e.ctrlKey);
47744                                 this.lastSelection = item;
47745                         }
47746                 }
47747     },
47748
47749     onItemClick : function(item, index, e){
47750                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47751                         return false;
47752                 }
47753                 return true;
47754     },
47755
47756     unselect : function(nodeInfo, suppressEvent){
47757                 var node = this.getNode(nodeInfo);
47758                 if(node && this.isSelected(node)){
47759                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47760                                 Roo.fly(node).removeClass(this.selectedClass);
47761                                 this.selections.remove(node);
47762                                 if(!suppressEvent){
47763                                         this.fireEvent("selectionchange", this, this.selections);
47764                                 }
47765                         }
47766                 }
47767     }
47768 });
47769 /*
47770  * Based on:
47771  * Ext JS Library 1.1.1
47772  * Copyright(c) 2006-2007, Ext JS, LLC.
47773  *
47774  * Originally Released Under LGPL - original licence link has changed is not relivant.
47775  *
47776  * Fork - LGPL
47777  * <script type="text/javascript">
47778  */
47779  
47780 /**
47781  * @class Roo.LayoutManager
47782  * @extends Roo.util.Observable
47783  * Base class for layout managers.
47784  */
47785 Roo.LayoutManager = function(container, config){
47786     Roo.LayoutManager.superclass.constructor.call(this);
47787     this.el = Roo.get(container);
47788     // ie scrollbar fix
47789     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47790         document.body.scroll = "no";
47791     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47792         this.el.position('relative');
47793     }
47794     this.id = this.el.id;
47795     this.el.addClass("x-layout-container");
47796     /** false to disable window resize monitoring @type Boolean */
47797     this.monitorWindowResize = true;
47798     this.regions = {};
47799     this.addEvents({
47800         /**
47801          * @event layout
47802          * Fires when a layout is performed. 
47803          * @param {Roo.LayoutManager} this
47804          */
47805         "layout" : true,
47806         /**
47807          * @event regionresized
47808          * Fires when the user resizes a region. 
47809          * @param {Roo.LayoutRegion} region The resized region
47810          * @param {Number} newSize The new size (width for east/west, height for north/south)
47811          */
47812         "regionresized" : true,
47813         /**
47814          * @event regioncollapsed
47815          * Fires when a region is collapsed. 
47816          * @param {Roo.LayoutRegion} region The collapsed region
47817          */
47818         "regioncollapsed" : true,
47819         /**
47820          * @event regionexpanded
47821          * Fires when a region is expanded.  
47822          * @param {Roo.LayoutRegion} region The expanded region
47823          */
47824         "regionexpanded" : true
47825     });
47826     this.updating = false;
47827     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47828 };
47829
47830 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47831     /**
47832      * Returns true if this layout is currently being updated
47833      * @return {Boolean}
47834      */
47835     isUpdating : function(){
47836         return this.updating; 
47837     },
47838     
47839     /**
47840      * Suspend the LayoutManager from doing auto-layouts while
47841      * making multiple add or remove calls
47842      */
47843     beginUpdate : function(){
47844         this.updating = true;    
47845     },
47846     
47847     /**
47848      * Restore auto-layouts and optionally disable the manager from performing a layout
47849      * @param {Boolean} noLayout true to disable a layout update 
47850      */
47851     endUpdate : function(noLayout){
47852         this.updating = false;
47853         if(!noLayout){
47854             this.layout();
47855         }    
47856     },
47857     
47858     layout: function(){
47859         
47860     },
47861     
47862     onRegionResized : function(region, newSize){
47863         this.fireEvent("regionresized", region, newSize);
47864         this.layout();
47865     },
47866     
47867     onRegionCollapsed : function(region){
47868         this.fireEvent("regioncollapsed", region);
47869     },
47870     
47871     onRegionExpanded : function(region){
47872         this.fireEvent("regionexpanded", region);
47873     },
47874         
47875     /**
47876      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47877      * performs box-model adjustments.
47878      * @return {Object} The size as an object {width: (the width), height: (the height)}
47879      */
47880     getViewSize : function(){
47881         var size;
47882         if(this.el.dom != document.body){
47883             size = this.el.getSize();
47884         }else{
47885             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47886         }
47887         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47888         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47889         return size;
47890     },
47891     
47892     /**
47893      * Returns the Element this layout is bound to.
47894      * @return {Roo.Element}
47895      */
47896     getEl : function(){
47897         return this.el;
47898     },
47899     
47900     /**
47901      * Returns the specified region.
47902      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47903      * @return {Roo.LayoutRegion}
47904      */
47905     getRegion : function(target){
47906         return this.regions[target.toLowerCase()];
47907     },
47908     
47909     onWindowResize : function(){
47910         if(this.monitorWindowResize){
47911             this.layout();
47912         }
47913     }
47914 });/*
47915  * Based on:
47916  * Ext JS Library 1.1.1
47917  * Copyright(c) 2006-2007, Ext JS, LLC.
47918  *
47919  * Originally Released Under LGPL - original licence link has changed is not relivant.
47920  *
47921  * Fork - LGPL
47922  * <script type="text/javascript">
47923  */
47924 /**
47925  * @class Roo.BorderLayout
47926  * @extends Roo.LayoutManager
47927  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47928  * please see: <br><br>
47929  * <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>
47930  * <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>
47931  * Example:
47932  <pre><code>
47933  var layout = new Roo.BorderLayout(document.body, {
47934     north: {
47935         initialSize: 25,
47936         titlebar: false
47937     },
47938     west: {
47939         split:true,
47940         initialSize: 200,
47941         minSize: 175,
47942         maxSize: 400,
47943         titlebar: true,
47944         collapsible: true
47945     },
47946     east: {
47947         split:true,
47948         initialSize: 202,
47949         minSize: 175,
47950         maxSize: 400,
47951         titlebar: true,
47952         collapsible: true
47953     },
47954     south: {
47955         split:true,
47956         initialSize: 100,
47957         minSize: 100,
47958         maxSize: 200,
47959         titlebar: true,
47960         collapsible: true
47961     },
47962     center: {
47963         titlebar: true,
47964         autoScroll:true,
47965         resizeTabs: true,
47966         minTabWidth: 50,
47967         preferredTabWidth: 150
47968     }
47969 });
47970
47971 // shorthand
47972 var CP = Roo.ContentPanel;
47973
47974 layout.beginUpdate();
47975 layout.add("north", new CP("north", "North"));
47976 layout.add("south", new CP("south", {title: "South", closable: true}));
47977 layout.add("west", new CP("west", {title: "West"}));
47978 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
47979 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
47980 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
47981 layout.getRegion("center").showPanel("center1");
47982 layout.endUpdate();
47983 </code></pre>
47984
47985 <b>The container the layout is rendered into can be either the body element or any other element.
47986 If it is not the body element, the container needs to either be an absolute positioned element,
47987 or you will need to add "position:relative" to the css of the container.  You will also need to specify
47988 the container size if it is not the body element.</b>
47989
47990 * @constructor
47991 * Create a new BorderLayout
47992 * @param {String/HTMLElement/Element} container The container this layout is bound to
47993 * @param {Object} config Configuration options
47994  */
47995 Roo.BorderLayout = function(container, config){
47996     config = config || {};
47997     Roo.BorderLayout.superclass.constructor.call(this, container, config);
47998     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
47999     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48000         var target = this.factory.validRegions[i];
48001         if(config[target]){
48002             this.addRegion(target, config[target]);
48003         }
48004     }
48005 };
48006
48007 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48008     /**
48009      * Creates and adds a new region if it doesn't already exist.
48010      * @param {String} target The target region key (north, south, east, west or center).
48011      * @param {Object} config The regions config object
48012      * @return {BorderLayoutRegion} The new region
48013      */
48014     addRegion : function(target, config){
48015         if(!this.regions[target]){
48016             var r = this.factory.create(target, this, config);
48017             this.bindRegion(target, r);
48018         }
48019         return this.regions[target];
48020     },
48021
48022     // private (kinda)
48023     bindRegion : function(name, r){
48024         this.regions[name] = r;
48025         r.on("visibilitychange", this.layout, this);
48026         r.on("paneladded", this.layout, this);
48027         r.on("panelremoved", this.layout, this);
48028         r.on("invalidated", this.layout, this);
48029         r.on("resized", this.onRegionResized, this);
48030         r.on("collapsed", this.onRegionCollapsed, this);
48031         r.on("expanded", this.onRegionExpanded, this);
48032     },
48033
48034     /**
48035      * Performs a layout update.
48036      */
48037     layout : function(){
48038         if(this.updating) return;
48039         var size = this.getViewSize();
48040         var w = size.width;
48041         var h = size.height;
48042         var centerW = w;
48043         var centerH = h;
48044         var centerY = 0;
48045         var centerX = 0;
48046         //var x = 0, y = 0;
48047
48048         var rs = this.regions;
48049         var north = rs["north"];
48050         var south = rs["south"]; 
48051         var west = rs["west"];
48052         var east = rs["east"];
48053         var center = rs["center"];
48054         //if(this.hideOnLayout){ // not supported anymore
48055             //c.el.setStyle("display", "none");
48056         //}
48057         if(north && north.isVisible()){
48058             var b = north.getBox();
48059             var m = north.getMargins();
48060             b.width = w - (m.left+m.right);
48061             b.x = m.left;
48062             b.y = m.top;
48063             centerY = b.height + b.y + m.bottom;
48064             centerH -= centerY;
48065             north.updateBox(this.safeBox(b));
48066         }
48067         if(south && south.isVisible()){
48068             var b = south.getBox();
48069             var m = south.getMargins();
48070             b.width = w - (m.left+m.right);
48071             b.x = m.left;
48072             var totalHeight = (b.height + m.top + m.bottom);
48073             b.y = h - totalHeight + m.top;
48074             centerH -= totalHeight;
48075             south.updateBox(this.safeBox(b));
48076         }
48077         if(west && west.isVisible()){
48078             var b = west.getBox();
48079             var m = west.getMargins();
48080             b.height = centerH - (m.top+m.bottom);
48081             b.x = m.left;
48082             b.y = centerY + m.top;
48083             var totalWidth = (b.width + m.left + m.right);
48084             centerX += totalWidth;
48085             centerW -= totalWidth;
48086             west.updateBox(this.safeBox(b));
48087         }
48088         if(east && east.isVisible()){
48089             var b = east.getBox();
48090             var m = east.getMargins();
48091             b.height = centerH - (m.top+m.bottom);
48092             var totalWidth = (b.width + m.left + m.right);
48093             b.x = w - totalWidth + m.left;
48094             b.y = centerY + m.top;
48095             centerW -= totalWidth;
48096             east.updateBox(this.safeBox(b));
48097         }
48098         if(center){
48099             var m = center.getMargins();
48100             var centerBox = {
48101                 x: centerX + m.left,
48102                 y: centerY + m.top,
48103                 width: centerW - (m.left+m.right),
48104                 height: centerH - (m.top+m.bottom)
48105             };
48106             //if(this.hideOnLayout){
48107                 //center.el.setStyle("display", "block");
48108             //}
48109             center.updateBox(this.safeBox(centerBox));
48110         }
48111         this.el.repaint();
48112         this.fireEvent("layout", this);
48113     },
48114
48115     // private
48116     safeBox : function(box){
48117         box.width = Math.max(0, box.width);
48118         box.height = Math.max(0, box.height);
48119         return box;
48120     },
48121
48122     /**
48123      * Adds a ContentPanel (or subclass) to this layout.
48124      * @param {String} target The target region key (north, south, east, west or center).
48125      * @param {Roo.ContentPanel} panel The panel to add
48126      * @return {Roo.ContentPanel} The added panel
48127      */
48128     add : function(target, panel){
48129          
48130         target = target.toLowerCase();
48131         return this.regions[target].add(panel);
48132     },
48133
48134     /**
48135      * Remove a ContentPanel (or subclass) to this layout.
48136      * @param {String} target The target region key (north, south, east, west or center).
48137      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48138      * @return {Roo.ContentPanel} The removed panel
48139      */
48140     remove : function(target, panel){
48141         target = target.toLowerCase();
48142         return this.regions[target].remove(panel);
48143     },
48144
48145     /**
48146      * Searches all regions for a panel with the specified id
48147      * @param {String} panelId
48148      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48149      */
48150     findPanel : function(panelId){
48151         var rs = this.regions;
48152         for(var target in rs){
48153             if(typeof rs[target] != "function"){
48154                 var p = rs[target].getPanel(panelId);
48155                 if(p){
48156                     return p;
48157                 }
48158             }
48159         }
48160         return null;
48161     },
48162
48163     /**
48164      * Searches all regions for a panel with the specified id and activates (shows) it.
48165      * @param {String/ContentPanel} panelId The panels id or the panel itself
48166      * @return {Roo.ContentPanel} The shown panel or null
48167      */
48168     showPanel : function(panelId) {
48169       var rs = this.regions;
48170       for(var target in rs){
48171          var r = rs[target];
48172          if(typeof r != "function"){
48173             if(r.hasPanel(panelId)){
48174                return r.showPanel(panelId);
48175             }
48176          }
48177       }
48178       return null;
48179    },
48180
48181    /**
48182      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48183      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48184      */
48185     restoreState : function(provider){
48186         if(!provider){
48187             provider = Roo.state.Manager;
48188         }
48189         var sm = new Roo.LayoutStateManager();
48190         sm.init(this, provider);
48191     },
48192
48193     /**
48194      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48195      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48196      * a valid ContentPanel config object.  Example:
48197      * <pre><code>
48198 // Create the main layout
48199 var layout = new Roo.BorderLayout('main-ct', {
48200     west: {
48201         split:true,
48202         minSize: 175,
48203         titlebar: true
48204     },
48205     center: {
48206         title:'Components'
48207     }
48208 }, 'main-ct');
48209
48210 // Create and add multiple ContentPanels at once via configs
48211 layout.batchAdd({
48212    west: {
48213        id: 'source-files',
48214        autoCreate:true,
48215        title:'Ext Source Files',
48216        autoScroll:true,
48217        fitToFrame:true
48218    },
48219    center : {
48220        el: cview,
48221        autoScroll:true,
48222        fitToFrame:true,
48223        toolbar: tb,
48224        resizeEl:'cbody'
48225    }
48226 });
48227 </code></pre>
48228      * @param {Object} regions An object containing ContentPanel configs by region name
48229      */
48230     batchAdd : function(regions){
48231         this.beginUpdate();
48232         for(var rname in regions){
48233             var lr = this.regions[rname];
48234             if(lr){
48235                 this.addTypedPanels(lr, regions[rname]);
48236             }
48237         }
48238         this.endUpdate();
48239     },
48240
48241     // private
48242     addTypedPanels : function(lr, ps){
48243         if(typeof ps == 'string'){
48244             lr.add(new Roo.ContentPanel(ps));
48245         }
48246         else if(ps instanceof Array){
48247             for(var i =0, len = ps.length; i < len; i++){
48248                 this.addTypedPanels(lr, ps[i]);
48249             }
48250         }
48251         else if(!ps.events){ // raw config?
48252             var el = ps.el;
48253             delete ps.el; // prevent conflict
48254             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48255         }
48256         else {  // panel object assumed!
48257             lr.add(ps);
48258         }
48259     },
48260     /**
48261      * Adds a xtype elements to the layout.
48262      * <pre><code>
48263
48264 layout.addxtype({
48265        xtype : 'ContentPanel',
48266        region: 'west',
48267        items: [ .... ]
48268    }
48269 );
48270
48271 layout.addxtype({
48272         xtype : 'NestedLayoutPanel',
48273         region: 'west',
48274         layout: {
48275            center: { },
48276            west: { }   
48277         },
48278         items : [ ... list of content panels or nested layout panels.. ]
48279    }
48280 );
48281 </code></pre>
48282      * @param {Object} cfg Xtype definition of item to add.
48283      */
48284     addxtype : function(cfg)
48285     {
48286         // basically accepts a pannel...
48287         // can accept a layout region..!?!?
48288         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48289         
48290         if (!cfg.xtype.match(/Panel$/)) {
48291             return false;
48292         }
48293         var ret = false;
48294         
48295         if (typeof(cfg.region) == 'undefined') {
48296             Roo.log("Failed to add Panel, region was not set");
48297             Roo.log(cfg);
48298             return false;
48299         }
48300         var region = cfg.region;
48301         delete cfg.region;
48302         
48303           
48304         var xitems = [];
48305         if (cfg.items) {
48306             xitems = cfg.items;
48307             delete cfg.items;
48308         }
48309         var nb = false;
48310         
48311         switch(cfg.xtype) 
48312         {
48313             case 'ContentPanel':  // ContentPanel (el, cfg)
48314             case 'ScrollPanel':  // ContentPanel (el, cfg)
48315             case 'ViewPanel': 
48316                 if(cfg.autoCreate) {
48317                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48318                 } else {
48319                     var el = this.el.createChild();
48320                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48321                 }
48322                 
48323                 this.add(region, ret);
48324                 break;
48325             
48326             
48327             case 'TreePanel': // our new panel!
48328                 cfg.el = this.el.createChild();
48329                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48330                 this.add(region, ret);
48331                 break;
48332             
48333             case 'NestedLayoutPanel': 
48334                 // create a new Layout (which is  a Border Layout...
48335                 var el = this.el.createChild();
48336                 var clayout = cfg.layout;
48337                 delete cfg.layout;
48338                 clayout.items   = clayout.items  || [];
48339                 // replace this exitems with the clayout ones..
48340                 xitems = clayout.items;
48341                  
48342                 
48343                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48344                     cfg.background = false;
48345                 }
48346                 var layout = new Roo.BorderLayout(el, clayout);
48347                 
48348                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48349                 //console.log('adding nested layout panel '  + cfg.toSource());
48350                 this.add(region, ret);
48351                 nb = {}; /// find first...
48352                 break;
48353                 
48354             case 'GridPanel': 
48355             
48356                 // needs grid and region
48357                 
48358                 //var el = this.getRegion(region).el.createChild();
48359                 var el = this.el.createChild();
48360                 // create the grid first...
48361                 
48362                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48363                 delete cfg.grid;
48364                 if (region == 'center' && this.active ) {
48365                     cfg.background = false;
48366                 }
48367                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48368                 
48369                 this.add(region, ret);
48370                 if (cfg.background) {
48371                     ret.on('activate', function(gp) {
48372                         if (!gp.grid.rendered) {
48373                             gp.grid.render();
48374                         }
48375                     });
48376                 } else {
48377                     grid.render();
48378                 }
48379                 break;
48380            
48381            
48382            
48383                 
48384                 
48385                 
48386             default: 
48387                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48388                 return null;
48389              // GridPanel (grid, cfg)
48390             
48391         }
48392         this.beginUpdate();
48393         // add children..
48394         var region = '';
48395         var abn = {};
48396         Roo.each(xitems, function(i)  {
48397             region = nb && i.region ? i.region : false;
48398             
48399             var add = ret.addxtype(i);
48400            
48401             if (region) {
48402                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48403                 if (!i.background) {
48404                     abn[region] = nb[region] ;
48405                 }
48406             }
48407             
48408         });
48409         this.endUpdate();
48410
48411         // make the last non-background panel active..
48412         //if (nb) { Roo.log(abn); }
48413         if (nb) {
48414             
48415             for(var r in abn) {
48416                 region = this.getRegion(r);
48417                 if (region) {
48418                     // tried using nb[r], but it does not work..
48419                      
48420                     region.showPanel(abn[r]);
48421                    
48422                 }
48423             }
48424         }
48425         return ret;
48426         
48427     }
48428 });
48429
48430 /**
48431  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48432  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48433  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48434  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48435  * <pre><code>
48436 // shorthand
48437 var CP = Roo.ContentPanel;
48438
48439 var layout = Roo.BorderLayout.create({
48440     north: {
48441         initialSize: 25,
48442         titlebar: false,
48443         panels: [new CP("north", "North")]
48444     },
48445     west: {
48446         split:true,
48447         initialSize: 200,
48448         minSize: 175,
48449         maxSize: 400,
48450         titlebar: true,
48451         collapsible: true,
48452         panels: [new CP("west", {title: "West"})]
48453     },
48454     east: {
48455         split:true,
48456         initialSize: 202,
48457         minSize: 175,
48458         maxSize: 400,
48459         titlebar: true,
48460         collapsible: true,
48461         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48462     },
48463     south: {
48464         split:true,
48465         initialSize: 100,
48466         minSize: 100,
48467         maxSize: 200,
48468         titlebar: true,
48469         collapsible: true,
48470         panels: [new CP("south", {title: "South", closable: true})]
48471     },
48472     center: {
48473         titlebar: true,
48474         autoScroll:true,
48475         resizeTabs: true,
48476         minTabWidth: 50,
48477         preferredTabWidth: 150,
48478         panels: [
48479             new CP("center1", {title: "Close Me", closable: true}),
48480             new CP("center2", {title: "Center Panel", closable: false})
48481         ]
48482     }
48483 }, document.body);
48484
48485 layout.getRegion("center").showPanel("center1");
48486 </code></pre>
48487  * @param config
48488  * @param targetEl
48489  */
48490 Roo.BorderLayout.create = function(config, targetEl){
48491     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48492     layout.beginUpdate();
48493     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48494     for(var j = 0, jlen = regions.length; j < jlen; j++){
48495         var lr = regions[j];
48496         if(layout.regions[lr] && config[lr].panels){
48497             var r = layout.regions[lr];
48498             var ps = config[lr].panels;
48499             layout.addTypedPanels(r, ps);
48500         }
48501     }
48502     layout.endUpdate();
48503     return layout;
48504 };
48505
48506 // private
48507 Roo.BorderLayout.RegionFactory = {
48508     // private
48509     validRegions : ["north","south","east","west","center"],
48510
48511     // private
48512     create : function(target, mgr, config){
48513         target = target.toLowerCase();
48514         if(config.lightweight || config.basic){
48515             return new Roo.BasicLayoutRegion(mgr, config, target);
48516         }
48517         switch(target){
48518             case "north":
48519                 return new Roo.NorthLayoutRegion(mgr, config);
48520             case "south":
48521                 return new Roo.SouthLayoutRegion(mgr, config);
48522             case "east":
48523                 return new Roo.EastLayoutRegion(mgr, config);
48524             case "west":
48525                 return new Roo.WestLayoutRegion(mgr, config);
48526             case "center":
48527                 return new Roo.CenterLayoutRegion(mgr, config);
48528         }
48529         throw 'Layout region "'+target+'" not supported.';
48530     }
48531 };/*
48532  * Based on:
48533  * Ext JS Library 1.1.1
48534  * Copyright(c) 2006-2007, Ext JS, LLC.
48535  *
48536  * Originally Released Under LGPL - original licence link has changed is not relivant.
48537  *
48538  * Fork - LGPL
48539  * <script type="text/javascript">
48540  */
48541  
48542 /**
48543  * @class Roo.BasicLayoutRegion
48544  * @extends Roo.util.Observable
48545  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48546  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48547  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48548  */
48549 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48550     this.mgr = mgr;
48551     this.position  = pos;
48552     this.events = {
48553         /**
48554          * @scope Roo.BasicLayoutRegion
48555          */
48556         
48557         /**
48558          * @event beforeremove
48559          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48560          * @param {Roo.LayoutRegion} this
48561          * @param {Roo.ContentPanel} panel The panel
48562          * @param {Object} e The cancel event object
48563          */
48564         "beforeremove" : true,
48565         /**
48566          * @event invalidated
48567          * Fires when the layout for this region is changed.
48568          * @param {Roo.LayoutRegion} this
48569          */
48570         "invalidated" : true,
48571         /**
48572          * @event visibilitychange
48573          * Fires when this region is shown or hidden 
48574          * @param {Roo.LayoutRegion} this
48575          * @param {Boolean} visibility true or false
48576          */
48577         "visibilitychange" : true,
48578         /**
48579          * @event paneladded
48580          * Fires when a panel is added. 
48581          * @param {Roo.LayoutRegion} this
48582          * @param {Roo.ContentPanel} panel The panel
48583          */
48584         "paneladded" : true,
48585         /**
48586          * @event panelremoved
48587          * Fires when a panel is removed. 
48588          * @param {Roo.LayoutRegion} this
48589          * @param {Roo.ContentPanel} panel The panel
48590          */
48591         "panelremoved" : true,
48592         /**
48593          * @event collapsed
48594          * Fires when this region is collapsed.
48595          * @param {Roo.LayoutRegion} this
48596          */
48597         "collapsed" : true,
48598         /**
48599          * @event expanded
48600          * Fires when this region is expanded.
48601          * @param {Roo.LayoutRegion} this
48602          */
48603         "expanded" : true,
48604         /**
48605          * @event slideshow
48606          * Fires when this region is slid into view.
48607          * @param {Roo.LayoutRegion} this
48608          */
48609         "slideshow" : true,
48610         /**
48611          * @event slidehide
48612          * Fires when this region slides out of view. 
48613          * @param {Roo.LayoutRegion} this
48614          */
48615         "slidehide" : true,
48616         /**
48617          * @event panelactivated
48618          * Fires when a panel is activated. 
48619          * @param {Roo.LayoutRegion} this
48620          * @param {Roo.ContentPanel} panel The activated panel
48621          */
48622         "panelactivated" : true,
48623         /**
48624          * @event resized
48625          * Fires when the user resizes this region. 
48626          * @param {Roo.LayoutRegion} this
48627          * @param {Number} newSize The new size (width for east/west, height for north/south)
48628          */
48629         "resized" : true
48630     };
48631     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48632     this.panels = new Roo.util.MixedCollection();
48633     this.panels.getKey = this.getPanelId.createDelegate(this);
48634     this.box = null;
48635     this.activePanel = null;
48636     // ensure listeners are added...
48637     
48638     if (config.listeners || config.events) {
48639         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48640             listeners : config.listeners || {},
48641             events : config.events || {}
48642         });
48643     }
48644     
48645     if(skipConfig !== true){
48646         this.applyConfig(config);
48647     }
48648 };
48649
48650 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48651     getPanelId : function(p){
48652         return p.getId();
48653     },
48654     
48655     applyConfig : function(config){
48656         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48657         this.config = config;
48658         
48659     },
48660     
48661     /**
48662      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48663      * the width, for horizontal (north, south) the height.
48664      * @param {Number} newSize The new width or height
48665      */
48666     resizeTo : function(newSize){
48667         var el = this.el ? this.el :
48668                  (this.activePanel ? this.activePanel.getEl() : null);
48669         if(el){
48670             switch(this.position){
48671                 case "east":
48672                 case "west":
48673                     el.setWidth(newSize);
48674                     this.fireEvent("resized", this, newSize);
48675                 break;
48676                 case "north":
48677                 case "south":
48678                     el.setHeight(newSize);
48679                     this.fireEvent("resized", this, newSize);
48680                 break;                
48681             }
48682         }
48683     },
48684     
48685     getBox : function(){
48686         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48687     },
48688     
48689     getMargins : function(){
48690         return this.margins;
48691     },
48692     
48693     updateBox : function(box){
48694         this.box = box;
48695         var el = this.activePanel.getEl();
48696         el.dom.style.left = box.x + "px";
48697         el.dom.style.top = box.y + "px";
48698         this.activePanel.setSize(box.width, box.height);
48699     },
48700     
48701     /**
48702      * Returns the container element for this region.
48703      * @return {Roo.Element}
48704      */
48705     getEl : function(){
48706         return this.activePanel;
48707     },
48708     
48709     /**
48710      * Returns true if this region is currently visible.
48711      * @return {Boolean}
48712      */
48713     isVisible : function(){
48714         return this.activePanel ? true : false;
48715     },
48716     
48717     setActivePanel : function(panel){
48718         panel = this.getPanel(panel);
48719         if(this.activePanel && this.activePanel != panel){
48720             this.activePanel.setActiveState(false);
48721             this.activePanel.getEl().setLeftTop(-10000,-10000);
48722         }
48723         this.activePanel = panel;
48724         panel.setActiveState(true);
48725         if(this.box){
48726             panel.setSize(this.box.width, this.box.height);
48727         }
48728         this.fireEvent("panelactivated", this, panel);
48729         this.fireEvent("invalidated");
48730     },
48731     
48732     /**
48733      * Show the specified panel.
48734      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48735      * @return {Roo.ContentPanel} The shown panel or null
48736      */
48737     showPanel : function(panel){
48738         if(panel = this.getPanel(panel)){
48739             this.setActivePanel(panel);
48740         }
48741         return panel;
48742     },
48743     
48744     /**
48745      * Get the active panel for this region.
48746      * @return {Roo.ContentPanel} The active panel or null
48747      */
48748     getActivePanel : function(){
48749         return this.activePanel;
48750     },
48751     
48752     /**
48753      * Add the passed ContentPanel(s)
48754      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48755      * @return {Roo.ContentPanel} The panel added (if only one was added)
48756      */
48757     add : function(panel){
48758         if(arguments.length > 1){
48759             for(var i = 0, len = arguments.length; i < len; i++) {
48760                 this.add(arguments[i]);
48761             }
48762             return null;
48763         }
48764         if(this.hasPanel(panel)){
48765             this.showPanel(panel);
48766             return panel;
48767         }
48768         var el = panel.getEl();
48769         if(el.dom.parentNode != this.mgr.el.dom){
48770             this.mgr.el.dom.appendChild(el.dom);
48771         }
48772         if(panel.setRegion){
48773             panel.setRegion(this);
48774         }
48775         this.panels.add(panel);
48776         el.setStyle("position", "absolute");
48777         if(!panel.background){
48778             this.setActivePanel(panel);
48779             if(this.config.initialSize && this.panels.getCount()==1){
48780                 this.resizeTo(this.config.initialSize);
48781             }
48782         }
48783         this.fireEvent("paneladded", this, panel);
48784         return panel;
48785     },
48786     
48787     /**
48788      * Returns true if the panel is in this region.
48789      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48790      * @return {Boolean}
48791      */
48792     hasPanel : function(panel){
48793         if(typeof panel == "object"){ // must be panel obj
48794             panel = panel.getId();
48795         }
48796         return this.getPanel(panel) ? true : false;
48797     },
48798     
48799     /**
48800      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48801      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48802      * @param {Boolean} preservePanel Overrides the config preservePanel option
48803      * @return {Roo.ContentPanel} The panel that was removed
48804      */
48805     remove : function(panel, preservePanel){
48806         panel = this.getPanel(panel);
48807         if(!panel){
48808             return null;
48809         }
48810         var e = {};
48811         this.fireEvent("beforeremove", this, panel, e);
48812         if(e.cancel === true){
48813             return null;
48814         }
48815         var panelId = panel.getId();
48816         this.panels.removeKey(panelId);
48817         return panel;
48818     },
48819     
48820     /**
48821      * Returns the panel specified or null if it's not in this region.
48822      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48823      * @return {Roo.ContentPanel}
48824      */
48825     getPanel : function(id){
48826         if(typeof id == "object"){ // must be panel obj
48827             return id;
48828         }
48829         return this.panels.get(id);
48830     },
48831     
48832     /**
48833      * Returns this regions position (north/south/east/west/center).
48834      * @return {String} 
48835      */
48836     getPosition: function(){
48837         return this.position;    
48838     }
48839 });/*
48840  * Based on:
48841  * Ext JS Library 1.1.1
48842  * Copyright(c) 2006-2007, Ext JS, LLC.
48843  *
48844  * Originally Released Under LGPL - original licence link has changed is not relivant.
48845  *
48846  * Fork - LGPL
48847  * <script type="text/javascript">
48848  */
48849  
48850 /**
48851  * @class Roo.LayoutRegion
48852  * @extends Roo.BasicLayoutRegion
48853  * This class represents a region in a layout manager.
48854  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48855  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48856  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48857  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48858  * @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})
48859  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48860  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48861  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48862  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48863  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48864  * @cfg {String}    title           The title for the region (overrides panel titles)
48865  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48866  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48867  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48868  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48869  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48870  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48871  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48872  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48873  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48874  * @cfg {Boolean}   showPin         True to show a pin button
48875  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48876  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48877  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48878  * @cfg {Number}    width           For East/West panels
48879  * @cfg {Number}    height          For North/South panels
48880  * @cfg {Boolean}   split           To show the splitter
48881  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48882  */
48883 Roo.LayoutRegion = function(mgr, config, pos){
48884     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48885     var dh = Roo.DomHelper;
48886     /** This region's container element 
48887     * @type Roo.Element */
48888     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48889     /** This region's title element 
48890     * @type Roo.Element */
48891
48892     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48893         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48894         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48895     ]}, true);
48896     this.titleEl.enableDisplayMode();
48897     /** This region's title text element 
48898     * @type HTMLElement */
48899     this.titleTextEl = this.titleEl.dom.firstChild;
48900     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48901     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48902     this.closeBtn.enableDisplayMode();
48903     this.closeBtn.on("click", this.closeClicked, this);
48904     this.closeBtn.hide();
48905
48906     this.createBody(config);
48907     this.visible = true;
48908     this.collapsed = false;
48909
48910     if(config.hideWhenEmpty){
48911         this.hide();
48912         this.on("paneladded", this.validateVisibility, this);
48913         this.on("panelremoved", this.validateVisibility, this);
48914     }
48915     this.applyConfig(config);
48916 };
48917
48918 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48919
48920     createBody : function(){
48921         /** This region's body element 
48922         * @type Roo.Element */
48923         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48924     },
48925
48926     applyConfig : function(c){
48927         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48928             var dh = Roo.DomHelper;
48929             if(c.titlebar !== false){
48930                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48931                 this.collapseBtn.on("click", this.collapse, this);
48932                 this.collapseBtn.enableDisplayMode();
48933
48934                 if(c.showPin === true || this.showPin){
48935                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48936                     this.stickBtn.enableDisplayMode();
48937                     this.stickBtn.on("click", this.expand, this);
48938                     this.stickBtn.hide();
48939                 }
48940             }
48941             /** This region's collapsed element
48942             * @type Roo.Element */
48943             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48944                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48945             ]}, true);
48946             if(c.floatable !== false){
48947                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48948                this.collapsedEl.on("click", this.collapseClick, this);
48949             }
48950
48951             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48952                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48953                    id: "message", unselectable: "on", style:{"float":"left"}});
48954                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48955              }
48956             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48957             this.expandBtn.on("click", this.expand, this);
48958         }
48959         if(this.collapseBtn){
48960             this.collapseBtn.setVisible(c.collapsible == true);
48961         }
48962         this.cmargins = c.cmargins || this.cmargins ||
48963                          (this.position == "west" || this.position == "east" ?
48964                              {top: 0, left: 2, right:2, bottom: 0} :
48965                              {top: 2, left: 0, right:0, bottom: 2});
48966         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48967         this.bottomTabs = c.tabPosition != "top";
48968         this.autoScroll = c.autoScroll || false;
48969         if(this.autoScroll){
48970             this.bodyEl.setStyle("overflow", "auto");
48971         }else{
48972             this.bodyEl.setStyle("overflow", "hidden");
48973         }
48974         //if(c.titlebar !== false){
48975             if((!c.titlebar && !c.title) || c.titlebar === false){
48976                 this.titleEl.hide();
48977             }else{
48978                 this.titleEl.show();
48979                 if(c.title){
48980                     this.titleTextEl.innerHTML = c.title;
48981                 }
48982             }
48983         //}
48984         this.duration = c.duration || .30;
48985         this.slideDuration = c.slideDuration || .45;
48986         this.config = c;
48987         if(c.collapsed){
48988             this.collapse(true);
48989         }
48990         if(c.hidden){
48991             this.hide();
48992         }
48993     },
48994     /**
48995      * Returns true if this region is currently visible.
48996      * @return {Boolean}
48997      */
48998     isVisible : function(){
48999         return this.visible;
49000     },
49001
49002     /**
49003      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49004      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49005      */
49006     setCollapsedTitle : function(title){
49007         title = title || "&#160;";
49008         if(this.collapsedTitleTextEl){
49009             this.collapsedTitleTextEl.innerHTML = title;
49010         }
49011     },
49012
49013     getBox : function(){
49014         var b;
49015         if(!this.collapsed){
49016             b = this.el.getBox(false, true);
49017         }else{
49018             b = this.collapsedEl.getBox(false, true);
49019         }
49020         return b;
49021     },
49022
49023     getMargins : function(){
49024         return this.collapsed ? this.cmargins : this.margins;
49025     },
49026
49027     highlight : function(){
49028         this.el.addClass("x-layout-panel-dragover");
49029     },
49030
49031     unhighlight : function(){
49032         this.el.removeClass("x-layout-panel-dragover");
49033     },
49034
49035     updateBox : function(box){
49036         this.box = box;
49037         if(!this.collapsed){
49038             this.el.dom.style.left = box.x + "px";
49039             this.el.dom.style.top = box.y + "px";
49040             this.updateBody(box.width, box.height);
49041         }else{
49042             this.collapsedEl.dom.style.left = box.x + "px";
49043             this.collapsedEl.dom.style.top = box.y + "px";
49044             this.collapsedEl.setSize(box.width, box.height);
49045         }
49046         if(this.tabs){
49047             this.tabs.autoSizeTabs();
49048         }
49049     },
49050
49051     updateBody : function(w, h){
49052         if(w !== null){
49053             this.el.setWidth(w);
49054             w -= this.el.getBorderWidth("rl");
49055             if(this.config.adjustments){
49056                 w += this.config.adjustments[0];
49057             }
49058         }
49059         if(h !== null){
49060             this.el.setHeight(h);
49061             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49062             h -= this.el.getBorderWidth("tb");
49063             if(this.config.adjustments){
49064                 h += this.config.adjustments[1];
49065             }
49066             this.bodyEl.setHeight(h);
49067             if(this.tabs){
49068                 h = this.tabs.syncHeight(h);
49069             }
49070         }
49071         if(this.panelSize){
49072             w = w !== null ? w : this.panelSize.width;
49073             h = h !== null ? h : this.panelSize.height;
49074         }
49075         if(this.activePanel){
49076             var el = this.activePanel.getEl();
49077             w = w !== null ? w : el.getWidth();
49078             h = h !== null ? h : el.getHeight();
49079             this.panelSize = {width: w, height: h};
49080             this.activePanel.setSize(w, h);
49081         }
49082         if(Roo.isIE && this.tabs){
49083             this.tabs.el.repaint();
49084         }
49085     },
49086
49087     /**
49088      * Returns the container element for this region.
49089      * @return {Roo.Element}
49090      */
49091     getEl : function(){
49092         return this.el;
49093     },
49094
49095     /**
49096      * Hides this region.
49097      */
49098     hide : function(){
49099         if(!this.collapsed){
49100             this.el.dom.style.left = "-2000px";
49101             this.el.hide();
49102         }else{
49103             this.collapsedEl.dom.style.left = "-2000px";
49104             this.collapsedEl.hide();
49105         }
49106         this.visible = false;
49107         this.fireEvent("visibilitychange", this, false);
49108     },
49109
49110     /**
49111      * Shows this region if it was previously hidden.
49112      */
49113     show : function(){
49114         if(!this.collapsed){
49115             this.el.show();
49116         }else{
49117             this.collapsedEl.show();
49118         }
49119         this.visible = true;
49120         this.fireEvent("visibilitychange", this, true);
49121     },
49122
49123     closeClicked : function(){
49124         if(this.activePanel){
49125             this.remove(this.activePanel);
49126         }
49127     },
49128
49129     collapseClick : function(e){
49130         if(this.isSlid){
49131            e.stopPropagation();
49132            this.slideIn();
49133         }else{
49134            e.stopPropagation();
49135            this.slideOut();
49136         }
49137     },
49138
49139     /**
49140      * Collapses this region.
49141      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49142      */
49143     collapse : function(skipAnim){
49144         if(this.collapsed) return;
49145         this.collapsed = true;
49146         if(this.split){
49147             this.split.el.hide();
49148         }
49149         if(this.config.animate && skipAnim !== true){
49150             this.fireEvent("invalidated", this);
49151             this.animateCollapse();
49152         }else{
49153             this.el.setLocation(-20000,-20000);
49154             this.el.hide();
49155             this.collapsedEl.show();
49156             this.fireEvent("collapsed", this);
49157             this.fireEvent("invalidated", this);
49158         }
49159     },
49160
49161     animateCollapse : function(){
49162         // overridden
49163     },
49164
49165     /**
49166      * Expands this region if it was previously collapsed.
49167      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49168      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49169      */
49170     expand : function(e, skipAnim){
49171         if(e) e.stopPropagation();
49172         if(!this.collapsed || this.el.hasActiveFx()) return;
49173         if(this.isSlid){
49174             this.afterSlideIn();
49175             skipAnim = true;
49176         }
49177         this.collapsed = false;
49178         if(this.config.animate && skipAnim !== true){
49179             this.animateExpand();
49180         }else{
49181             this.el.show();
49182             if(this.split){
49183                 this.split.el.show();
49184             }
49185             this.collapsedEl.setLocation(-2000,-2000);
49186             this.collapsedEl.hide();
49187             this.fireEvent("invalidated", this);
49188             this.fireEvent("expanded", this);
49189         }
49190     },
49191
49192     animateExpand : function(){
49193         // overridden
49194     },
49195
49196     initTabs : function()
49197     {
49198         this.bodyEl.setStyle("overflow", "hidden");
49199         var ts = new Roo.TabPanel(
49200                 this.bodyEl.dom,
49201                 {
49202                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49203                     disableTooltips: this.config.disableTabTips,
49204                     toolbar : this.config.toolbar
49205                 }
49206         );
49207         if(this.config.hideTabs){
49208             ts.stripWrap.setDisplayed(false);
49209         }
49210         this.tabs = ts;
49211         ts.resizeTabs = this.config.resizeTabs === true;
49212         ts.minTabWidth = this.config.minTabWidth || 40;
49213         ts.maxTabWidth = this.config.maxTabWidth || 250;
49214         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49215         ts.monitorResize = false;
49216         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49217         ts.bodyEl.addClass('x-layout-tabs-body');
49218         this.panels.each(this.initPanelAsTab, this);
49219     },
49220
49221     initPanelAsTab : function(panel){
49222         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49223                     this.config.closeOnTab && panel.isClosable());
49224         if(panel.tabTip !== undefined){
49225             ti.setTooltip(panel.tabTip);
49226         }
49227         ti.on("activate", function(){
49228               this.setActivePanel(panel);
49229         }, this);
49230         if(this.config.closeOnTab){
49231             ti.on("beforeclose", function(t, e){
49232                 e.cancel = true;
49233                 this.remove(panel);
49234             }, this);
49235         }
49236         return ti;
49237     },
49238
49239     updatePanelTitle : function(panel, title){
49240         if(this.activePanel == panel){
49241             this.updateTitle(title);
49242         }
49243         if(this.tabs){
49244             var ti = this.tabs.getTab(panel.getEl().id);
49245             ti.setText(title);
49246             if(panel.tabTip !== undefined){
49247                 ti.setTooltip(panel.tabTip);
49248             }
49249         }
49250     },
49251
49252     updateTitle : function(title){
49253         if(this.titleTextEl && !this.config.title){
49254             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49255         }
49256     },
49257
49258     setActivePanel : function(panel){
49259         panel = this.getPanel(panel);
49260         if(this.activePanel && this.activePanel != panel){
49261             this.activePanel.setActiveState(false);
49262         }
49263         this.activePanel = panel;
49264         panel.setActiveState(true);
49265         if(this.panelSize){
49266             panel.setSize(this.panelSize.width, this.panelSize.height);
49267         }
49268         if(this.closeBtn){
49269             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49270         }
49271         this.updateTitle(panel.getTitle());
49272         if(this.tabs){
49273             this.fireEvent("invalidated", this);
49274         }
49275         this.fireEvent("panelactivated", this, panel);
49276     },
49277
49278     /**
49279      * Shows the specified panel.
49280      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49281      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49282      */
49283     showPanel : function(panel){
49284         if(panel = this.getPanel(panel)){
49285             if(this.tabs){
49286                 var tab = this.tabs.getTab(panel.getEl().id);
49287                 if(tab.isHidden()){
49288                     this.tabs.unhideTab(tab.id);
49289                 }
49290                 tab.activate();
49291             }else{
49292                 this.setActivePanel(panel);
49293             }
49294         }
49295         return panel;
49296     },
49297
49298     /**
49299      * Get the active panel for this region.
49300      * @return {Roo.ContentPanel} The active panel or null
49301      */
49302     getActivePanel : function(){
49303         return this.activePanel;
49304     },
49305
49306     validateVisibility : function(){
49307         if(this.panels.getCount() < 1){
49308             this.updateTitle("&#160;");
49309             this.closeBtn.hide();
49310             this.hide();
49311         }else{
49312             if(!this.isVisible()){
49313                 this.show();
49314             }
49315         }
49316     },
49317
49318     /**
49319      * Adds the passed ContentPanel(s) to this region.
49320      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49321      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49322      */
49323     add : function(panel){
49324         if(arguments.length > 1){
49325             for(var i = 0, len = arguments.length; i < len; i++) {
49326                 this.add(arguments[i]);
49327             }
49328             return null;
49329         }
49330         if(this.hasPanel(panel)){
49331             this.showPanel(panel);
49332             return panel;
49333         }
49334         panel.setRegion(this);
49335         this.panels.add(panel);
49336         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49337             this.bodyEl.dom.appendChild(panel.getEl().dom);
49338             if(panel.background !== true){
49339                 this.setActivePanel(panel);
49340             }
49341             this.fireEvent("paneladded", this, panel);
49342             return panel;
49343         }
49344         if(!this.tabs){
49345             this.initTabs();
49346         }else{
49347             this.initPanelAsTab(panel);
49348         }
49349         if(panel.background !== true){
49350             this.tabs.activate(panel.getEl().id);
49351         }
49352         this.fireEvent("paneladded", this, panel);
49353         return panel;
49354     },
49355
49356     /**
49357      * Hides the tab for the specified panel.
49358      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49359      */
49360     hidePanel : function(panel){
49361         if(this.tabs && (panel = this.getPanel(panel))){
49362             this.tabs.hideTab(panel.getEl().id);
49363         }
49364     },
49365
49366     /**
49367      * Unhides the tab for a previously hidden panel.
49368      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49369      */
49370     unhidePanel : function(panel){
49371         if(this.tabs && (panel = this.getPanel(panel))){
49372             this.tabs.unhideTab(panel.getEl().id);
49373         }
49374     },
49375
49376     clearPanels : function(){
49377         while(this.panels.getCount() > 0){
49378              this.remove(this.panels.first());
49379         }
49380     },
49381
49382     /**
49383      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49384      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49385      * @param {Boolean} preservePanel Overrides the config preservePanel option
49386      * @return {Roo.ContentPanel} The panel that was removed
49387      */
49388     remove : function(panel, preservePanel){
49389         panel = this.getPanel(panel);
49390         if(!panel){
49391             return null;
49392         }
49393         var e = {};
49394         this.fireEvent("beforeremove", this, panel, e);
49395         if(e.cancel === true){
49396             return null;
49397         }
49398         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49399         var panelId = panel.getId();
49400         this.panels.removeKey(panelId);
49401         if(preservePanel){
49402             document.body.appendChild(panel.getEl().dom);
49403         }
49404         if(this.tabs){
49405             this.tabs.removeTab(panel.getEl().id);
49406         }else if (!preservePanel){
49407             this.bodyEl.dom.removeChild(panel.getEl().dom);
49408         }
49409         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49410             var p = this.panels.first();
49411             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49412             tempEl.appendChild(p.getEl().dom);
49413             this.bodyEl.update("");
49414             this.bodyEl.dom.appendChild(p.getEl().dom);
49415             tempEl = null;
49416             this.updateTitle(p.getTitle());
49417             this.tabs = null;
49418             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49419             this.setActivePanel(p);
49420         }
49421         panel.setRegion(null);
49422         if(this.activePanel == panel){
49423             this.activePanel = null;
49424         }
49425         if(this.config.autoDestroy !== false && preservePanel !== true){
49426             try{panel.destroy();}catch(e){}
49427         }
49428         this.fireEvent("panelremoved", this, panel);
49429         return panel;
49430     },
49431
49432     /**
49433      * Returns the TabPanel component used by this region
49434      * @return {Roo.TabPanel}
49435      */
49436     getTabs : function(){
49437         return this.tabs;
49438     },
49439
49440     createTool : function(parentEl, className){
49441         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49442             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49443         btn.addClassOnOver("x-layout-tools-button-over");
49444         return btn;
49445     }
49446 });/*
49447  * Based on:
49448  * Ext JS Library 1.1.1
49449  * Copyright(c) 2006-2007, Ext JS, LLC.
49450  *
49451  * Originally Released Under LGPL - original licence link has changed is not relivant.
49452  *
49453  * Fork - LGPL
49454  * <script type="text/javascript">
49455  */
49456  
49457
49458
49459 /**
49460  * @class Roo.SplitLayoutRegion
49461  * @extends Roo.LayoutRegion
49462  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49463  */
49464 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49465     this.cursor = cursor;
49466     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49467 };
49468
49469 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49470     splitTip : "Drag to resize.",
49471     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49472     useSplitTips : false,
49473
49474     applyConfig : function(config){
49475         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49476         if(config.split){
49477             if(!this.split){
49478                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49479                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49480                 /** The SplitBar for this region 
49481                 * @type Roo.SplitBar */
49482                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49483                 this.split.on("moved", this.onSplitMove, this);
49484                 this.split.useShim = config.useShim === true;
49485                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49486                 if(this.useSplitTips){
49487                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49488                 }
49489                 if(config.collapsible){
49490                     this.split.el.on("dblclick", this.collapse,  this);
49491                 }
49492             }
49493             if(typeof config.minSize != "undefined"){
49494                 this.split.minSize = config.minSize;
49495             }
49496             if(typeof config.maxSize != "undefined"){
49497                 this.split.maxSize = config.maxSize;
49498             }
49499             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49500                 this.hideSplitter();
49501             }
49502         }
49503     },
49504
49505     getHMaxSize : function(){
49506          var cmax = this.config.maxSize || 10000;
49507          var center = this.mgr.getRegion("center");
49508          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49509     },
49510
49511     getVMaxSize : function(){
49512          var cmax = this.config.maxSize || 10000;
49513          var center = this.mgr.getRegion("center");
49514          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49515     },
49516
49517     onSplitMove : function(split, newSize){
49518         this.fireEvent("resized", this, newSize);
49519     },
49520     
49521     /** 
49522      * Returns the {@link Roo.SplitBar} for this region.
49523      * @return {Roo.SplitBar}
49524      */
49525     getSplitBar : function(){
49526         return this.split;
49527     },
49528     
49529     hide : function(){
49530         this.hideSplitter();
49531         Roo.SplitLayoutRegion.superclass.hide.call(this);
49532     },
49533
49534     hideSplitter : function(){
49535         if(this.split){
49536             this.split.el.setLocation(-2000,-2000);
49537             this.split.el.hide();
49538         }
49539     },
49540
49541     show : function(){
49542         if(this.split){
49543             this.split.el.show();
49544         }
49545         Roo.SplitLayoutRegion.superclass.show.call(this);
49546     },
49547     
49548     beforeSlide: function(){
49549         if(Roo.isGecko){// firefox overflow auto bug workaround
49550             this.bodyEl.clip();
49551             if(this.tabs) this.tabs.bodyEl.clip();
49552             if(this.activePanel){
49553                 this.activePanel.getEl().clip();
49554                 
49555                 if(this.activePanel.beforeSlide){
49556                     this.activePanel.beforeSlide();
49557                 }
49558             }
49559         }
49560     },
49561     
49562     afterSlide : function(){
49563         if(Roo.isGecko){// firefox overflow auto bug workaround
49564             this.bodyEl.unclip();
49565             if(this.tabs) this.tabs.bodyEl.unclip();
49566             if(this.activePanel){
49567                 this.activePanel.getEl().unclip();
49568                 if(this.activePanel.afterSlide){
49569                     this.activePanel.afterSlide();
49570                 }
49571             }
49572         }
49573     },
49574
49575     initAutoHide : function(){
49576         if(this.autoHide !== false){
49577             if(!this.autoHideHd){
49578                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49579                 this.autoHideHd = {
49580                     "mouseout": function(e){
49581                         if(!e.within(this.el, true)){
49582                             st.delay(500);
49583                         }
49584                     },
49585                     "mouseover" : function(e){
49586                         st.cancel();
49587                     },
49588                     scope : this
49589                 };
49590             }
49591             this.el.on(this.autoHideHd);
49592         }
49593     },
49594
49595     clearAutoHide : function(){
49596         if(this.autoHide !== false){
49597             this.el.un("mouseout", this.autoHideHd.mouseout);
49598             this.el.un("mouseover", this.autoHideHd.mouseover);
49599         }
49600     },
49601
49602     clearMonitor : function(){
49603         Roo.get(document).un("click", this.slideInIf, this);
49604     },
49605
49606     // these names are backwards but not changed for compat
49607     slideOut : function(){
49608         if(this.isSlid || this.el.hasActiveFx()){
49609             return;
49610         }
49611         this.isSlid = true;
49612         if(this.collapseBtn){
49613             this.collapseBtn.hide();
49614         }
49615         this.closeBtnState = this.closeBtn.getStyle('display');
49616         this.closeBtn.hide();
49617         if(this.stickBtn){
49618             this.stickBtn.show();
49619         }
49620         this.el.show();
49621         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49622         this.beforeSlide();
49623         this.el.setStyle("z-index", 10001);
49624         this.el.slideIn(this.getSlideAnchor(), {
49625             callback: function(){
49626                 this.afterSlide();
49627                 this.initAutoHide();
49628                 Roo.get(document).on("click", this.slideInIf, this);
49629                 this.fireEvent("slideshow", this);
49630             },
49631             scope: this,
49632             block: true
49633         });
49634     },
49635
49636     afterSlideIn : function(){
49637         this.clearAutoHide();
49638         this.isSlid = false;
49639         this.clearMonitor();
49640         this.el.setStyle("z-index", "");
49641         if(this.collapseBtn){
49642             this.collapseBtn.show();
49643         }
49644         this.closeBtn.setStyle('display', this.closeBtnState);
49645         if(this.stickBtn){
49646             this.stickBtn.hide();
49647         }
49648         this.fireEvent("slidehide", this);
49649     },
49650
49651     slideIn : function(cb){
49652         if(!this.isSlid || this.el.hasActiveFx()){
49653             Roo.callback(cb);
49654             return;
49655         }
49656         this.isSlid = false;
49657         this.beforeSlide();
49658         this.el.slideOut(this.getSlideAnchor(), {
49659             callback: function(){
49660                 this.el.setLeftTop(-10000, -10000);
49661                 this.afterSlide();
49662                 this.afterSlideIn();
49663                 Roo.callback(cb);
49664             },
49665             scope: this,
49666             block: true
49667         });
49668     },
49669     
49670     slideInIf : function(e){
49671         if(!e.within(this.el)){
49672             this.slideIn();
49673         }
49674     },
49675
49676     animateCollapse : function(){
49677         this.beforeSlide();
49678         this.el.setStyle("z-index", 20000);
49679         var anchor = this.getSlideAnchor();
49680         this.el.slideOut(anchor, {
49681             callback : function(){
49682                 this.el.setStyle("z-index", "");
49683                 this.collapsedEl.slideIn(anchor, {duration:.3});
49684                 this.afterSlide();
49685                 this.el.setLocation(-10000,-10000);
49686                 this.el.hide();
49687                 this.fireEvent("collapsed", this);
49688             },
49689             scope: this,
49690             block: true
49691         });
49692     },
49693
49694     animateExpand : function(){
49695         this.beforeSlide();
49696         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49697         this.el.setStyle("z-index", 20000);
49698         this.collapsedEl.hide({
49699             duration:.1
49700         });
49701         this.el.slideIn(this.getSlideAnchor(), {
49702             callback : function(){
49703                 this.el.setStyle("z-index", "");
49704                 this.afterSlide();
49705                 if(this.split){
49706                     this.split.el.show();
49707                 }
49708                 this.fireEvent("invalidated", this);
49709                 this.fireEvent("expanded", this);
49710             },
49711             scope: this,
49712             block: true
49713         });
49714     },
49715
49716     anchors : {
49717         "west" : "left",
49718         "east" : "right",
49719         "north" : "top",
49720         "south" : "bottom"
49721     },
49722
49723     sanchors : {
49724         "west" : "l",
49725         "east" : "r",
49726         "north" : "t",
49727         "south" : "b"
49728     },
49729
49730     canchors : {
49731         "west" : "tl-tr",
49732         "east" : "tr-tl",
49733         "north" : "tl-bl",
49734         "south" : "bl-tl"
49735     },
49736
49737     getAnchor : function(){
49738         return this.anchors[this.position];
49739     },
49740
49741     getCollapseAnchor : function(){
49742         return this.canchors[this.position];
49743     },
49744
49745     getSlideAnchor : function(){
49746         return this.sanchors[this.position];
49747     },
49748
49749     getAlignAdj : function(){
49750         var cm = this.cmargins;
49751         switch(this.position){
49752             case "west":
49753                 return [0, 0];
49754             break;
49755             case "east":
49756                 return [0, 0];
49757             break;
49758             case "north":
49759                 return [0, 0];
49760             break;
49761             case "south":
49762                 return [0, 0];
49763             break;
49764         }
49765     },
49766
49767     getExpandAdj : function(){
49768         var c = this.collapsedEl, cm = this.cmargins;
49769         switch(this.position){
49770             case "west":
49771                 return [-(cm.right+c.getWidth()+cm.left), 0];
49772             break;
49773             case "east":
49774                 return [cm.right+c.getWidth()+cm.left, 0];
49775             break;
49776             case "north":
49777                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49778             break;
49779             case "south":
49780                 return [0, cm.top+cm.bottom+c.getHeight()];
49781             break;
49782         }
49783     }
49784 });/*
49785  * Based on:
49786  * Ext JS Library 1.1.1
49787  * Copyright(c) 2006-2007, Ext JS, LLC.
49788  *
49789  * Originally Released Under LGPL - original licence link has changed is not relivant.
49790  *
49791  * Fork - LGPL
49792  * <script type="text/javascript">
49793  */
49794 /*
49795  * These classes are private internal classes
49796  */
49797 Roo.CenterLayoutRegion = function(mgr, config){
49798     Roo.LayoutRegion.call(this, mgr, config, "center");
49799     this.visible = true;
49800     this.minWidth = config.minWidth || 20;
49801     this.minHeight = config.minHeight || 20;
49802 };
49803
49804 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49805     hide : function(){
49806         // center panel can't be hidden
49807     },
49808     
49809     show : function(){
49810         // center panel can't be hidden
49811     },
49812     
49813     getMinWidth: function(){
49814         return this.minWidth;
49815     },
49816     
49817     getMinHeight: function(){
49818         return this.minHeight;
49819     }
49820 });
49821
49822
49823 Roo.NorthLayoutRegion = function(mgr, config){
49824     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49825     if(this.split){
49826         this.split.placement = Roo.SplitBar.TOP;
49827         this.split.orientation = Roo.SplitBar.VERTICAL;
49828         this.split.el.addClass("x-layout-split-v");
49829     }
49830     var size = config.initialSize || config.height;
49831     if(typeof size != "undefined"){
49832         this.el.setHeight(size);
49833     }
49834 };
49835 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49836     orientation: Roo.SplitBar.VERTICAL,
49837     getBox : function(){
49838         if(this.collapsed){
49839             return this.collapsedEl.getBox();
49840         }
49841         var box = this.el.getBox();
49842         if(this.split){
49843             box.height += this.split.el.getHeight();
49844         }
49845         return box;
49846     },
49847     
49848     updateBox : function(box){
49849         if(this.split && !this.collapsed){
49850             box.height -= this.split.el.getHeight();
49851             this.split.el.setLeft(box.x);
49852             this.split.el.setTop(box.y+box.height);
49853             this.split.el.setWidth(box.width);
49854         }
49855         if(this.collapsed){
49856             this.updateBody(box.width, null);
49857         }
49858         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49859     }
49860 });
49861
49862 Roo.SouthLayoutRegion = function(mgr, config){
49863     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49864     if(this.split){
49865         this.split.placement = Roo.SplitBar.BOTTOM;
49866         this.split.orientation = Roo.SplitBar.VERTICAL;
49867         this.split.el.addClass("x-layout-split-v");
49868     }
49869     var size = config.initialSize || config.height;
49870     if(typeof size != "undefined"){
49871         this.el.setHeight(size);
49872     }
49873 };
49874 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49875     orientation: Roo.SplitBar.VERTICAL,
49876     getBox : function(){
49877         if(this.collapsed){
49878             return this.collapsedEl.getBox();
49879         }
49880         var box = this.el.getBox();
49881         if(this.split){
49882             var sh = this.split.el.getHeight();
49883             box.height += sh;
49884             box.y -= sh;
49885         }
49886         return box;
49887     },
49888     
49889     updateBox : function(box){
49890         if(this.split && !this.collapsed){
49891             var sh = this.split.el.getHeight();
49892             box.height -= sh;
49893             box.y += sh;
49894             this.split.el.setLeft(box.x);
49895             this.split.el.setTop(box.y-sh);
49896             this.split.el.setWidth(box.width);
49897         }
49898         if(this.collapsed){
49899             this.updateBody(box.width, null);
49900         }
49901         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49902     }
49903 });
49904
49905 Roo.EastLayoutRegion = function(mgr, config){
49906     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49907     if(this.split){
49908         this.split.placement = Roo.SplitBar.RIGHT;
49909         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49910         this.split.el.addClass("x-layout-split-h");
49911     }
49912     var size = config.initialSize || config.width;
49913     if(typeof size != "undefined"){
49914         this.el.setWidth(size);
49915     }
49916 };
49917 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49918     orientation: Roo.SplitBar.HORIZONTAL,
49919     getBox : function(){
49920         if(this.collapsed){
49921             return this.collapsedEl.getBox();
49922         }
49923         var box = this.el.getBox();
49924         if(this.split){
49925             var sw = this.split.el.getWidth();
49926             box.width += sw;
49927             box.x -= sw;
49928         }
49929         return box;
49930     },
49931
49932     updateBox : function(box){
49933         if(this.split && !this.collapsed){
49934             var sw = this.split.el.getWidth();
49935             box.width -= sw;
49936             this.split.el.setLeft(box.x);
49937             this.split.el.setTop(box.y);
49938             this.split.el.setHeight(box.height);
49939             box.x += sw;
49940         }
49941         if(this.collapsed){
49942             this.updateBody(null, box.height);
49943         }
49944         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49945     }
49946 });
49947
49948 Roo.WestLayoutRegion = function(mgr, config){
49949     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49950     if(this.split){
49951         this.split.placement = Roo.SplitBar.LEFT;
49952         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49953         this.split.el.addClass("x-layout-split-h");
49954     }
49955     var size = config.initialSize || config.width;
49956     if(typeof size != "undefined"){
49957         this.el.setWidth(size);
49958     }
49959 };
49960 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49961     orientation: Roo.SplitBar.HORIZONTAL,
49962     getBox : function(){
49963         if(this.collapsed){
49964             return this.collapsedEl.getBox();
49965         }
49966         var box = this.el.getBox();
49967         if(this.split){
49968             box.width += this.split.el.getWidth();
49969         }
49970         return box;
49971     },
49972     
49973     updateBox : function(box){
49974         if(this.split && !this.collapsed){
49975             var sw = this.split.el.getWidth();
49976             box.width -= sw;
49977             this.split.el.setLeft(box.x+box.width);
49978             this.split.el.setTop(box.y);
49979             this.split.el.setHeight(box.height);
49980         }
49981         if(this.collapsed){
49982             this.updateBody(null, box.height);
49983         }
49984         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49985     }
49986 });
49987 /*
49988  * Based on:
49989  * Ext JS Library 1.1.1
49990  * Copyright(c) 2006-2007, Ext JS, LLC.
49991  *
49992  * Originally Released Under LGPL - original licence link has changed is not relivant.
49993  *
49994  * Fork - LGPL
49995  * <script type="text/javascript">
49996  */
49997  
49998  
49999 /*
50000  * Private internal class for reading and applying state
50001  */
50002 Roo.LayoutStateManager = function(layout){
50003      // default empty state
50004      this.state = {
50005         north: {},
50006         south: {},
50007         east: {},
50008         west: {}       
50009     };
50010 };
50011
50012 Roo.LayoutStateManager.prototype = {
50013     init : function(layout, provider){
50014         this.provider = provider;
50015         var state = provider.get(layout.id+"-layout-state");
50016         if(state){
50017             var wasUpdating = layout.isUpdating();
50018             if(!wasUpdating){
50019                 layout.beginUpdate();
50020             }
50021             for(var key in state){
50022                 if(typeof state[key] != "function"){
50023                     var rstate = state[key];
50024                     var r = layout.getRegion(key);
50025                     if(r && rstate){
50026                         if(rstate.size){
50027                             r.resizeTo(rstate.size);
50028                         }
50029                         if(rstate.collapsed == true){
50030                             r.collapse(true);
50031                         }else{
50032                             r.expand(null, true);
50033                         }
50034                     }
50035                 }
50036             }
50037             if(!wasUpdating){
50038                 layout.endUpdate();
50039             }
50040             this.state = state; 
50041         }
50042         this.layout = layout;
50043         layout.on("regionresized", this.onRegionResized, this);
50044         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50045         layout.on("regionexpanded", this.onRegionExpanded, this);
50046     },
50047     
50048     storeState : function(){
50049         this.provider.set(this.layout.id+"-layout-state", this.state);
50050     },
50051     
50052     onRegionResized : function(region, newSize){
50053         this.state[region.getPosition()].size = newSize;
50054         this.storeState();
50055     },
50056     
50057     onRegionCollapsed : function(region){
50058         this.state[region.getPosition()].collapsed = true;
50059         this.storeState();
50060     },
50061     
50062     onRegionExpanded : function(region){
50063         this.state[region.getPosition()].collapsed = false;
50064         this.storeState();
50065     }
50066 };/*
50067  * Based on:
50068  * Ext JS Library 1.1.1
50069  * Copyright(c) 2006-2007, Ext JS, LLC.
50070  *
50071  * Originally Released Under LGPL - original licence link has changed is not relivant.
50072  *
50073  * Fork - LGPL
50074  * <script type="text/javascript">
50075  */
50076 /**
50077  * @class Roo.ContentPanel
50078  * @extends Roo.util.Observable
50079  * A basic ContentPanel element.
50080  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50081  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50082  * @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
50083  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50084  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50085  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50086  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50087  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50088  * @cfg {String} title          The title for this panel
50089  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50090  * @cfg {String} url            Calls {@link #setUrl} with this value
50091  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50092  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50093  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50094  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50095
50096  * @constructor
50097  * Create a new ContentPanel.
50098  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50099  * @param {String/Object} config A string to set only the title or a config object
50100  * @param {String} content (optional) Set the HTML content for this panel
50101  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50102  */
50103 Roo.ContentPanel = function(el, config, content){
50104     
50105      
50106     /*
50107     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50108         config = el;
50109         el = Roo.id();
50110     }
50111     if (config && config.parentLayout) { 
50112         el = config.parentLayout.el.createChild(); 
50113     }
50114     */
50115     if(el.autoCreate){ // xtype is available if this is called from factory
50116         config = el;
50117         el = Roo.id();
50118     }
50119     this.el = Roo.get(el);
50120     if(!this.el && config && config.autoCreate){
50121         if(typeof config.autoCreate == "object"){
50122             if(!config.autoCreate.id){
50123                 config.autoCreate.id = config.id||el;
50124             }
50125             this.el = Roo.DomHelper.append(document.body,
50126                         config.autoCreate, true);
50127         }else{
50128             this.el = Roo.DomHelper.append(document.body,
50129                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50130         }
50131     }
50132     this.closable = false;
50133     this.loaded = false;
50134     this.active = false;
50135     if(typeof config == "string"){
50136         this.title = config;
50137     }else{
50138         Roo.apply(this, config);
50139     }
50140     
50141     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50142         this.wrapEl = this.el.wrap();
50143         this.toolbar.container = this.el.insertSibling(false, 'before');
50144         this.toolbar = new Roo.Toolbar(this.toolbar);
50145     }
50146     
50147     // xtype created footer. - not sure if will work as we normally have to render first..
50148     if (this.footer && !this.footer.el && this.footer.xtype) {
50149         if (!this.wrapEl) {
50150             this.wrapEl = this.el.wrap();
50151         }
50152     
50153         this.footer.container = this.wrapEl.createChild();
50154          
50155         this.footer = Roo.factory(this.footer, Roo);
50156         
50157     }
50158     
50159     if(this.resizeEl){
50160         this.resizeEl = Roo.get(this.resizeEl, true);
50161     }else{
50162         this.resizeEl = this.el;
50163     }
50164     // handle view.xtype
50165     
50166  
50167     
50168     
50169     this.addEvents({
50170         /**
50171          * @event activate
50172          * Fires when this panel is activated. 
50173          * @param {Roo.ContentPanel} this
50174          */
50175         "activate" : true,
50176         /**
50177          * @event deactivate
50178          * Fires when this panel is activated. 
50179          * @param {Roo.ContentPanel} this
50180          */
50181         "deactivate" : true,
50182
50183         /**
50184          * @event resize
50185          * Fires when this panel is resized if fitToFrame is true.
50186          * @param {Roo.ContentPanel} this
50187          * @param {Number} width The width after any component adjustments
50188          * @param {Number} height The height after any component adjustments
50189          */
50190         "resize" : true,
50191         
50192          /**
50193          * @event render
50194          * Fires when this tab is created
50195          * @param {Roo.ContentPanel} this
50196          */
50197         "render" : true
50198         
50199         
50200         
50201     });
50202     
50203
50204     
50205     
50206     if(this.autoScroll){
50207         this.resizeEl.setStyle("overflow", "auto");
50208     } else {
50209         // fix randome scrolling
50210         this.el.on('scroll', function() {
50211             Roo.log('fix random scolling');
50212             this.scrollTo('top',0); 
50213         });
50214     }
50215     content = content || this.content;
50216     if(content){
50217         this.setContent(content);
50218     }
50219     if(config && config.url){
50220         this.setUrl(this.url, this.params, this.loadOnce);
50221     }
50222     
50223     
50224     
50225     Roo.ContentPanel.superclass.constructor.call(this);
50226     
50227     if (this.view && typeof(this.view.xtype) != 'undefined') {
50228         this.view.el = this.el.appendChild(document.createElement("div"));
50229         this.view = Roo.factory(this.view); 
50230         this.view.render  &&  this.view.render(false, '');  
50231     }
50232     
50233     
50234     this.fireEvent('render', this);
50235 };
50236
50237 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50238     tabTip:'',
50239     setRegion : function(region){
50240         this.region = region;
50241         if(region){
50242            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50243         }else{
50244            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50245         } 
50246     },
50247     
50248     /**
50249      * Returns the toolbar for this Panel if one was configured. 
50250      * @return {Roo.Toolbar} 
50251      */
50252     getToolbar : function(){
50253         return this.toolbar;
50254     },
50255     
50256     setActiveState : function(active){
50257         this.active = active;
50258         if(!active){
50259             this.fireEvent("deactivate", this);
50260         }else{
50261             this.fireEvent("activate", this);
50262         }
50263     },
50264     /**
50265      * Updates this panel's element
50266      * @param {String} content The new content
50267      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50268     */
50269     setContent : function(content, loadScripts){
50270         this.el.update(content, loadScripts);
50271     },
50272
50273     ignoreResize : function(w, h){
50274         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50275             return true;
50276         }else{
50277             this.lastSize = {width: w, height: h};
50278             return false;
50279         }
50280     },
50281     /**
50282      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50283      * @return {Roo.UpdateManager} The UpdateManager
50284      */
50285     getUpdateManager : function(){
50286         return this.el.getUpdateManager();
50287     },
50288      /**
50289      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50290      * @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:
50291 <pre><code>
50292 panel.load({
50293     url: "your-url.php",
50294     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50295     callback: yourFunction,
50296     scope: yourObject, //(optional scope)
50297     discardUrl: false,
50298     nocache: false,
50299     text: "Loading...",
50300     timeout: 30,
50301     scripts: false
50302 });
50303 </code></pre>
50304      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50305      * 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.
50306      * @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}
50307      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50308      * @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.
50309      * @return {Roo.ContentPanel} this
50310      */
50311     load : function(){
50312         var um = this.el.getUpdateManager();
50313         um.update.apply(um, arguments);
50314         return this;
50315     },
50316
50317
50318     /**
50319      * 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.
50320      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50321      * @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)
50322      * @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)
50323      * @return {Roo.UpdateManager} The UpdateManager
50324      */
50325     setUrl : function(url, params, loadOnce){
50326         if(this.refreshDelegate){
50327             this.removeListener("activate", this.refreshDelegate);
50328         }
50329         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50330         this.on("activate", this.refreshDelegate);
50331         return this.el.getUpdateManager();
50332     },
50333     
50334     _handleRefresh : function(url, params, loadOnce){
50335         if(!loadOnce || !this.loaded){
50336             var updater = this.el.getUpdateManager();
50337             updater.update(url, params, this._setLoaded.createDelegate(this));
50338         }
50339     },
50340     
50341     _setLoaded : function(){
50342         this.loaded = true;
50343     }, 
50344     
50345     /**
50346      * Returns this panel's id
50347      * @return {String} 
50348      */
50349     getId : function(){
50350         return this.el.id;
50351     },
50352     
50353     /** 
50354      * Returns this panel's element - used by regiosn to add.
50355      * @return {Roo.Element} 
50356      */
50357     getEl : function(){
50358         return this.wrapEl || this.el;
50359     },
50360     
50361     adjustForComponents : function(width, height)
50362     {
50363         //Roo.log('adjustForComponents ');
50364         if(this.resizeEl != this.el){
50365             width -= this.el.getFrameWidth('lr');
50366             height -= this.el.getFrameWidth('tb');
50367         }
50368         if(this.toolbar){
50369             var te = this.toolbar.getEl();
50370             height -= te.getHeight();
50371             te.setWidth(width);
50372         }
50373         if(this.footer){
50374             var te = this.footer.getEl();
50375             Roo.log("footer:" + te.getHeight());
50376             
50377             height -= te.getHeight();
50378             te.setWidth(width);
50379         }
50380         
50381         
50382         if(this.adjustments){
50383             width += this.adjustments[0];
50384             height += this.adjustments[1];
50385         }
50386         return {"width": width, "height": height};
50387     },
50388     
50389     setSize : function(width, height){
50390         if(this.fitToFrame && !this.ignoreResize(width, height)){
50391             if(this.fitContainer && this.resizeEl != this.el){
50392                 this.el.setSize(width, height);
50393             }
50394             var size = this.adjustForComponents(width, height);
50395             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50396             this.fireEvent('resize', this, size.width, size.height);
50397         }
50398     },
50399     
50400     /**
50401      * Returns this panel's title
50402      * @return {String} 
50403      */
50404     getTitle : function(){
50405         return this.title;
50406     },
50407     
50408     /**
50409      * Set this panel's title
50410      * @param {String} title
50411      */
50412     setTitle : function(title){
50413         this.title = title;
50414         if(this.region){
50415             this.region.updatePanelTitle(this, title);
50416         }
50417     },
50418     
50419     /**
50420      * Returns true is this panel was configured to be closable
50421      * @return {Boolean} 
50422      */
50423     isClosable : function(){
50424         return this.closable;
50425     },
50426     
50427     beforeSlide : function(){
50428         this.el.clip();
50429         this.resizeEl.clip();
50430     },
50431     
50432     afterSlide : function(){
50433         this.el.unclip();
50434         this.resizeEl.unclip();
50435     },
50436     
50437     /**
50438      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50439      *   Will fail silently if the {@link #setUrl} method has not been called.
50440      *   This does not activate the panel, just updates its content.
50441      */
50442     refresh : function(){
50443         if(this.refreshDelegate){
50444            this.loaded = false;
50445            this.refreshDelegate();
50446         }
50447     },
50448     
50449     /**
50450      * Destroys this panel
50451      */
50452     destroy : function(){
50453         this.el.removeAllListeners();
50454         var tempEl = document.createElement("span");
50455         tempEl.appendChild(this.el.dom);
50456         tempEl.innerHTML = "";
50457         this.el.remove();
50458         this.el = null;
50459     },
50460     
50461     /**
50462      * form - if the content panel contains a form - this is a reference to it.
50463      * @type {Roo.form.Form}
50464      */
50465     form : false,
50466     /**
50467      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50468      *    This contains a reference to it.
50469      * @type {Roo.View}
50470      */
50471     view : false,
50472     
50473       /**
50474      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50475      * <pre><code>
50476
50477 layout.addxtype({
50478        xtype : 'Form',
50479        items: [ .... ]
50480    }
50481 );
50482
50483 </code></pre>
50484      * @param {Object} cfg Xtype definition of item to add.
50485      */
50486     
50487     addxtype : function(cfg) {
50488         // add form..
50489         if (cfg.xtype.match(/^Form$/)) {
50490             
50491             var el;
50492             //if (this.footer) {
50493             //    el = this.footer.container.insertSibling(false, 'before');
50494             //} else {
50495                 el = this.el.createChild();
50496             //}
50497
50498             this.form = new  Roo.form.Form(cfg);
50499             
50500             
50501             if ( this.form.allItems.length) this.form.render(el.dom);
50502             return this.form;
50503         }
50504         // should only have one of theses..
50505         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50506             // views.. should not be just added - used named prop 'view''
50507             
50508             cfg.el = this.el.appendChild(document.createElement("div"));
50509             // factory?
50510             
50511             var ret = new Roo.factory(cfg);
50512              
50513              ret.render && ret.render(false, ''); // render blank..
50514             this.view = ret;
50515             return ret;
50516         }
50517         return false;
50518     }
50519 });
50520
50521 /**
50522  * @class Roo.GridPanel
50523  * @extends Roo.ContentPanel
50524  * @constructor
50525  * Create a new GridPanel.
50526  * @param {Roo.grid.Grid} grid The grid for this panel
50527  * @param {String/Object} config A string to set only the panel's title, or a config object
50528  */
50529 Roo.GridPanel = function(grid, config){
50530     
50531   
50532     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50533         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50534         
50535     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50536     
50537     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50538     
50539     if(this.toolbar){
50540         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50541     }
50542     // xtype created footer. - not sure if will work as we normally have to render first..
50543     if (this.footer && !this.footer.el && this.footer.xtype) {
50544         
50545         this.footer.container = this.grid.getView().getFooterPanel(true);
50546         this.footer.dataSource = this.grid.dataSource;
50547         this.footer = Roo.factory(this.footer, Roo);
50548         
50549     }
50550     
50551     grid.monitorWindowResize = false; // turn off autosizing
50552     grid.autoHeight = false;
50553     grid.autoWidth = false;
50554     this.grid = grid;
50555     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50556 };
50557
50558 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50559     getId : function(){
50560         return this.grid.id;
50561     },
50562     
50563     /**
50564      * Returns the grid for this panel
50565      * @return {Roo.grid.Grid} 
50566      */
50567     getGrid : function(){
50568         return this.grid;    
50569     },
50570     
50571     setSize : function(width, height){
50572         if(!this.ignoreResize(width, height)){
50573             var grid = this.grid;
50574             var size = this.adjustForComponents(width, height);
50575             grid.getGridEl().setSize(size.width, size.height);
50576             grid.autoSize();
50577         }
50578     },
50579     
50580     beforeSlide : function(){
50581         this.grid.getView().scroller.clip();
50582     },
50583     
50584     afterSlide : function(){
50585         this.grid.getView().scroller.unclip();
50586     },
50587     
50588     destroy : function(){
50589         this.grid.destroy();
50590         delete this.grid;
50591         Roo.GridPanel.superclass.destroy.call(this); 
50592     }
50593 });
50594
50595
50596 /**
50597  * @class Roo.NestedLayoutPanel
50598  * @extends Roo.ContentPanel
50599  * @constructor
50600  * Create a new NestedLayoutPanel.
50601  * 
50602  * 
50603  * @param {Roo.BorderLayout} layout The layout for this panel
50604  * @param {String/Object} config A string to set only the title or a config object
50605  */
50606 Roo.NestedLayoutPanel = function(layout, config)
50607 {
50608     // construct with only one argument..
50609     /* FIXME - implement nicer consturctors
50610     if (layout.layout) {
50611         config = layout;
50612         layout = config.layout;
50613         delete config.layout;
50614     }
50615     if (layout.xtype && !layout.getEl) {
50616         // then layout needs constructing..
50617         layout = Roo.factory(layout, Roo);
50618     }
50619     */
50620     
50621     
50622     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50623     
50624     layout.monitorWindowResize = false; // turn off autosizing
50625     this.layout = layout;
50626     this.layout.getEl().addClass("x-layout-nested-layout");
50627     
50628     
50629     
50630     
50631 };
50632
50633 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50634
50635     setSize : function(width, height){
50636         if(!this.ignoreResize(width, height)){
50637             var size = this.adjustForComponents(width, height);
50638             var el = this.layout.getEl();
50639             el.setSize(size.width, size.height);
50640             var touch = el.dom.offsetWidth;
50641             this.layout.layout();
50642             // ie requires a double layout on the first pass
50643             if(Roo.isIE && !this.initialized){
50644                 this.initialized = true;
50645                 this.layout.layout();
50646             }
50647         }
50648     },
50649     
50650     // activate all subpanels if not currently active..
50651     
50652     setActiveState : function(active){
50653         this.active = active;
50654         if(!active){
50655             this.fireEvent("deactivate", this);
50656             return;
50657         }
50658         
50659         this.fireEvent("activate", this);
50660         // not sure if this should happen before or after..
50661         if (!this.layout) {
50662             return; // should not happen..
50663         }
50664         var reg = false;
50665         for (var r in this.layout.regions) {
50666             reg = this.layout.getRegion(r);
50667             if (reg.getActivePanel()) {
50668                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50669                 reg.setActivePanel(reg.getActivePanel());
50670                 continue;
50671             }
50672             if (!reg.panels.length) {
50673                 continue;
50674             }
50675             reg.showPanel(reg.getPanel(0));
50676         }
50677         
50678         
50679         
50680         
50681     },
50682     
50683     /**
50684      * Returns the nested BorderLayout for this panel
50685      * @return {Roo.BorderLayout} 
50686      */
50687     getLayout : function(){
50688         return this.layout;
50689     },
50690     
50691      /**
50692      * Adds a xtype elements to the layout of the nested panel
50693      * <pre><code>
50694
50695 panel.addxtype({
50696        xtype : 'ContentPanel',
50697        region: 'west',
50698        items: [ .... ]
50699    }
50700 );
50701
50702 panel.addxtype({
50703         xtype : 'NestedLayoutPanel',
50704         region: 'west',
50705         layout: {
50706            center: { },
50707            west: { }   
50708         },
50709         items : [ ... list of content panels or nested layout panels.. ]
50710    }
50711 );
50712 </code></pre>
50713      * @param {Object} cfg Xtype definition of item to add.
50714      */
50715     addxtype : function(cfg) {
50716         return this.layout.addxtype(cfg);
50717     
50718     }
50719 });
50720
50721 Roo.ScrollPanel = function(el, config, content){
50722     config = config || {};
50723     config.fitToFrame = true;
50724     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50725     
50726     this.el.dom.style.overflow = "hidden";
50727     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50728     this.el.removeClass("x-layout-inactive-content");
50729     this.el.on("mousewheel", this.onWheel, this);
50730
50731     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50732     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50733     up.unselectable(); down.unselectable();
50734     up.on("click", this.scrollUp, this);
50735     down.on("click", this.scrollDown, this);
50736     up.addClassOnOver("x-scroller-btn-over");
50737     down.addClassOnOver("x-scroller-btn-over");
50738     up.addClassOnClick("x-scroller-btn-click");
50739     down.addClassOnClick("x-scroller-btn-click");
50740     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50741
50742     this.resizeEl = this.el;
50743     this.el = wrap; this.up = up; this.down = down;
50744 };
50745
50746 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50747     increment : 100,
50748     wheelIncrement : 5,
50749     scrollUp : function(){
50750         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50751     },
50752
50753     scrollDown : function(){
50754         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50755     },
50756
50757     afterScroll : function(){
50758         var el = this.resizeEl;
50759         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50760         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50761         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50762     },
50763
50764     setSize : function(){
50765         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50766         this.afterScroll();
50767     },
50768
50769     onWheel : function(e){
50770         var d = e.getWheelDelta();
50771         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50772         this.afterScroll();
50773         e.stopEvent();
50774     },
50775
50776     setContent : function(content, loadScripts){
50777         this.resizeEl.update(content, loadScripts);
50778     }
50779
50780 });
50781
50782
50783
50784
50785
50786
50787
50788
50789
50790 /**
50791  * @class Roo.TreePanel
50792  * @extends Roo.ContentPanel
50793  * @constructor
50794  * Create a new TreePanel. - defaults to fit/scoll contents.
50795  * @param {String/Object} config A string to set only the panel's title, or a config object
50796  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50797  */
50798 Roo.TreePanel = function(config){
50799     var el = config.el;
50800     var tree = config.tree;
50801     delete config.tree; 
50802     delete config.el; // hopefull!
50803     
50804     // wrapper for IE7 strict & safari scroll issue
50805     
50806     var treeEl = el.createChild();
50807     config.resizeEl = treeEl;
50808     
50809     
50810     
50811     Roo.TreePanel.superclass.constructor.call(this, el, config);
50812  
50813  
50814     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50815     //console.log(tree);
50816     this.on('activate', function()
50817     {
50818         if (this.tree.rendered) {
50819             return;
50820         }
50821         //console.log('render tree');
50822         this.tree.render();
50823     });
50824     // this should not be needed.. - it's actually the 'el' that resizes?
50825     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50826     
50827     //this.on('resize',  function (cp, w, h) {
50828     //        this.tree.innerCt.setWidth(w);
50829     //        this.tree.innerCt.setHeight(h);
50830     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50831     //});
50832
50833         
50834     
50835 };
50836
50837 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50838     fitToFrame : true,
50839     autoScroll : true
50840 });
50841
50842
50843
50844
50845
50846
50847
50848
50849
50850
50851
50852 /*
50853  * Based on:
50854  * Ext JS Library 1.1.1
50855  * Copyright(c) 2006-2007, Ext JS, LLC.
50856  *
50857  * Originally Released Under LGPL - original licence link has changed is not relivant.
50858  *
50859  * Fork - LGPL
50860  * <script type="text/javascript">
50861  */
50862  
50863
50864 /**
50865  * @class Roo.ReaderLayout
50866  * @extends Roo.BorderLayout
50867  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50868  * center region containing two nested regions (a top one for a list view and one for item preview below),
50869  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50870  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50871  * expedites the setup of the overall layout and regions for this common application style.
50872  * Example:
50873  <pre><code>
50874 var reader = new Roo.ReaderLayout();
50875 var CP = Roo.ContentPanel;  // shortcut for adding
50876
50877 reader.beginUpdate();
50878 reader.add("north", new CP("north", "North"));
50879 reader.add("west", new CP("west", {title: "West"}));
50880 reader.add("east", new CP("east", {title: "East"}));
50881
50882 reader.regions.listView.add(new CP("listView", "List"));
50883 reader.regions.preview.add(new CP("preview", "Preview"));
50884 reader.endUpdate();
50885 </code></pre>
50886 * @constructor
50887 * Create a new ReaderLayout
50888 * @param {Object} config Configuration options
50889 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50890 * document.body if omitted)
50891 */
50892 Roo.ReaderLayout = function(config, renderTo){
50893     var c = config || {size:{}};
50894     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50895         north: c.north !== false ? Roo.apply({
50896             split:false,
50897             initialSize: 32,
50898             titlebar: false
50899         }, c.north) : false,
50900         west: c.west !== false ? Roo.apply({
50901             split:true,
50902             initialSize: 200,
50903             minSize: 175,
50904             maxSize: 400,
50905             titlebar: true,
50906             collapsible: true,
50907             animate: true,
50908             margins:{left:5,right:0,bottom:5,top:5},
50909             cmargins:{left:5,right:5,bottom:5,top:5}
50910         }, c.west) : false,
50911         east: c.east !== false ? Roo.apply({
50912             split:true,
50913             initialSize: 200,
50914             minSize: 175,
50915             maxSize: 400,
50916             titlebar: true,
50917             collapsible: true,
50918             animate: true,
50919             margins:{left:0,right:5,bottom:5,top:5},
50920             cmargins:{left:5,right:5,bottom:5,top:5}
50921         }, c.east) : false,
50922         center: Roo.apply({
50923             tabPosition: 'top',
50924             autoScroll:false,
50925             closeOnTab: true,
50926             titlebar:false,
50927             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50928         }, c.center)
50929     });
50930
50931     this.el.addClass('x-reader');
50932
50933     this.beginUpdate();
50934
50935     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50936         south: c.preview !== false ? Roo.apply({
50937             split:true,
50938             initialSize: 200,
50939             minSize: 100,
50940             autoScroll:true,
50941             collapsible:true,
50942             titlebar: true,
50943             cmargins:{top:5,left:0, right:0, bottom:0}
50944         }, c.preview) : false,
50945         center: Roo.apply({
50946             autoScroll:false,
50947             titlebar:false,
50948             minHeight:200
50949         }, c.listView)
50950     });
50951     this.add('center', new Roo.NestedLayoutPanel(inner,
50952             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50953
50954     this.endUpdate();
50955
50956     this.regions.preview = inner.getRegion('south');
50957     this.regions.listView = inner.getRegion('center');
50958 };
50959
50960 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50961  * Based on:
50962  * Ext JS Library 1.1.1
50963  * Copyright(c) 2006-2007, Ext JS, LLC.
50964  *
50965  * Originally Released Under LGPL - original licence link has changed is not relivant.
50966  *
50967  * Fork - LGPL
50968  * <script type="text/javascript">
50969  */
50970  
50971 /**
50972  * @class Roo.grid.Grid
50973  * @extends Roo.util.Observable
50974  * This class represents the primary interface of a component based grid control.
50975  * <br><br>Usage:<pre><code>
50976  var grid = new Roo.grid.Grid("my-container-id", {
50977      ds: myDataStore,
50978      cm: myColModel,
50979      selModel: mySelectionModel,
50980      autoSizeColumns: true,
50981      monitorWindowResize: false,
50982      trackMouseOver: true
50983  });
50984  // set any options
50985  grid.render();
50986  * </code></pre>
50987  * <b>Common Problems:</b><br/>
50988  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
50989  * element will correct this<br/>
50990  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
50991  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
50992  * are unpredictable.<br/>
50993  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
50994  * grid to calculate dimensions/offsets.<br/>
50995   * @constructor
50996  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50997  * The container MUST have some type of size defined for the grid to fill. The container will be
50998  * automatically set to position relative if it isn't already.
50999  * @param {Object} config A config object that sets properties on this grid.
51000  */
51001 Roo.grid.Grid = function(container, config){
51002         // initialize the container
51003         this.container = Roo.get(container);
51004         this.container.update("");
51005         this.container.setStyle("overflow", "hidden");
51006     this.container.addClass('x-grid-container');
51007
51008     this.id = this.container.id;
51009
51010     Roo.apply(this, config);
51011     // check and correct shorthanded configs
51012     if(this.ds){
51013         this.dataSource = this.ds;
51014         delete this.ds;
51015     }
51016     if(this.cm){
51017         this.colModel = this.cm;
51018         delete this.cm;
51019     }
51020     if(this.sm){
51021         this.selModel = this.sm;
51022         delete this.sm;
51023     }
51024
51025     if (this.selModel) {
51026         this.selModel = Roo.factory(this.selModel, Roo.grid);
51027         this.sm = this.selModel;
51028         this.sm.xmodule = this.xmodule || false;
51029     }
51030     if (typeof(this.colModel.config) == 'undefined') {
51031         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51032         this.cm = this.colModel;
51033         this.cm.xmodule = this.xmodule || false;
51034     }
51035     if (this.dataSource) {
51036         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51037         this.ds = this.dataSource;
51038         this.ds.xmodule = this.xmodule || false;
51039          
51040     }
51041     
51042     
51043     
51044     if(this.width){
51045         this.container.setWidth(this.width);
51046     }
51047
51048     if(this.height){
51049         this.container.setHeight(this.height);
51050     }
51051     /** @private */
51052         this.addEvents({
51053         // raw events
51054         /**
51055          * @event click
51056          * The raw click event for the entire grid.
51057          * @param {Roo.EventObject} e
51058          */
51059         "click" : true,
51060         /**
51061          * @event dblclick
51062          * The raw dblclick event for the entire grid.
51063          * @param {Roo.EventObject} e
51064          */
51065         "dblclick" : true,
51066         /**
51067          * @event contextmenu
51068          * The raw contextmenu event for the entire grid.
51069          * @param {Roo.EventObject} e
51070          */
51071         "contextmenu" : true,
51072         /**
51073          * @event mousedown
51074          * The raw mousedown event for the entire grid.
51075          * @param {Roo.EventObject} e
51076          */
51077         "mousedown" : true,
51078         /**
51079          * @event mouseup
51080          * The raw mouseup event for the entire grid.
51081          * @param {Roo.EventObject} e
51082          */
51083         "mouseup" : true,
51084         /**
51085          * @event mouseover
51086          * The raw mouseover event for the entire grid.
51087          * @param {Roo.EventObject} e
51088          */
51089         "mouseover" : true,
51090         /**
51091          * @event mouseout
51092          * The raw mouseout event for the entire grid.
51093          * @param {Roo.EventObject} e
51094          */
51095         "mouseout" : true,
51096         /**
51097          * @event keypress
51098          * The raw keypress event for the entire grid.
51099          * @param {Roo.EventObject} e
51100          */
51101         "keypress" : true,
51102         /**
51103          * @event keydown
51104          * The raw keydown event for the entire grid.
51105          * @param {Roo.EventObject} e
51106          */
51107         "keydown" : true,
51108
51109         // custom events
51110
51111         /**
51112          * @event cellclick
51113          * Fires when a cell is clicked
51114          * @param {Grid} this
51115          * @param {Number} rowIndex
51116          * @param {Number} columnIndex
51117          * @param {Roo.EventObject} e
51118          */
51119         "cellclick" : true,
51120         /**
51121          * @event celldblclick
51122          * Fires when a cell is double clicked
51123          * @param {Grid} this
51124          * @param {Number} rowIndex
51125          * @param {Number} columnIndex
51126          * @param {Roo.EventObject} e
51127          */
51128         "celldblclick" : true,
51129         /**
51130          * @event rowclick
51131          * Fires when a row is clicked
51132          * @param {Grid} this
51133          * @param {Number} rowIndex
51134          * @param {Roo.EventObject} e
51135          */
51136         "rowclick" : true,
51137         /**
51138          * @event rowdblclick
51139          * Fires when a row is double clicked
51140          * @param {Grid} this
51141          * @param {Number} rowIndex
51142          * @param {Roo.EventObject} e
51143          */
51144         "rowdblclick" : true,
51145         /**
51146          * @event headerclick
51147          * Fires when a header is clicked
51148          * @param {Grid} this
51149          * @param {Number} columnIndex
51150          * @param {Roo.EventObject} e
51151          */
51152         "headerclick" : true,
51153         /**
51154          * @event headerdblclick
51155          * Fires when a header cell is double clicked
51156          * @param {Grid} this
51157          * @param {Number} columnIndex
51158          * @param {Roo.EventObject} e
51159          */
51160         "headerdblclick" : true,
51161         /**
51162          * @event rowcontextmenu
51163          * Fires when a row is right clicked
51164          * @param {Grid} this
51165          * @param {Number} rowIndex
51166          * @param {Roo.EventObject} e
51167          */
51168         "rowcontextmenu" : true,
51169         /**
51170          * @event cellcontextmenu
51171          * Fires when a cell is right clicked
51172          * @param {Grid} this
51173          * @param {Number} rowIndex
51174          * @param {Number} cellIndex
51175          * @param {Roo.EventObject} e
51176          */
51177          "cellcontextmenu" : true,
51178         /**
51179          * @event headercontextmenu
51180          * Fires when a header is right clicked
51181          * @param {Grid} this
51182          * @param {Number} columnIndex
51183          * @param {Roo.EventObject} e
51184          */
51185         "headercontextmenu" : true,
51186         /**
51187          * @event bodyscroll
51188          * Fires when the body element is scrolled
51189          * @param {Number} scrollLeft
51190          * @param {Number} scrollTop
51191          */
51192         "bodyscroll" : true,
51193         /**
51194          * @event columnresize
51195          * Fires when the user resizes a column
51196          * @param {Number} columnIndex
51197          * @param {Number} newSize
51198          */
51199         "columnresize" : true,
51200         /**
51201          * @event columnmove
51202          * Fires when the user moves a column
51203          * @param {Number} oldIndex
51204          * @param {Number} newIndex
51205          */
51206         "columnmove" : true,
51207         /**
51208          * @event startdrag
51209          * Fires when row(s) start being dragged
51210          * @param {Grid} this
51211          * @param {Roo.GridDD} dd The drag drop object
51212          * @param {event} e The raw browser event
51213          */
51214         "startdrag" : true,
51215         /**
51216          * @event enddrag
51217          * Fires when a drag operation is complete
51218          * @param {Grid} this
51219          * @param {Roo.GridDD} dd The drag drop object
51220          * @param {event} e The raw browser event
51221          */
51222         "enddrag" : true,
51223         /**
51224          * @event dragdrop
51225          * Fires when dragged row(s) are dropped on a valid DD target
51226          * @param {Grid} this
51227          * @param {Roo.GridDD} dd The drag drop object
51228          * @param {String} targetId The target drag drop object
51229          * @param {event} e The raw browser event
51230          */
51231         "dragdrop" : true,
51232         /**
51233          * @event dragover
51234          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51235          * @param {Grid} this
51236          * @param {Roo.GridDD} dd The drag drop object
51237          * @param {String} targetId The target drag drop object
51238          * @param {event} e The raw browser event
51239          */
51240         "dragover" : true,
51241         /**
51242          * @event dragenter
51243          *  Fires when the dragged row(s) first cross another DD target while being dragged
51244          * @param {Grid} this
51245          * @param {Roo.GridDD} dd The drag drop object
51246          * @param {String} targetId The target drag drop object
51247          * @param {event} e The raw browser event
51248          */
51249         "dragenter" : true,
51250         /**
51251          * @event dragout
51252          * Fires when the dragged row(s) leave another DD target while being dragged
51253          * @param {Grid} this
51254          * @param {Roo.GridDD} dd The drag drop object
51255          * @param {String} targetId The target drag drop object
51256          * @param {event} e The raw browser event
51257          */
51258         "dragout" : true,
51259         /**
51260          * @event rowclass
51261          * Fires when a row is rendered, so you can change add a style to it.
51262          * @param {GridView} gridview   The grid view
51263          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51264          */
51265         'rowclass' : true,
51266
51267         /**
51268          * @event render
51269          * Fires when the grid is rendered
51270          * @param {Grid} grid
51271          */
51272         'render' : true
51273     });
51274
51275     Roo.grid.Grid.superclass.constructor.call(this);
51276 };
51277 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51278     
51279     /**
51280      * @cfg {String} ddGroup - drag drop group.
51281      */
51282
51283     /**
51284      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51285      */
51286     minColumnWidth : 25,
51287
51288     /**
51289      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51290      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51291      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51292      */
51293     autoSizeColumns : false,
51294
51295     /**
51296      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51297      */
51298     autoSizeHeaders : true,
51299
51300     /**
51301      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51302      */
51303     monitorWindowResize : true,
51304
51305     /**
51306      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51307      * rows measured to get a columns size. Default is 0 (all rows).
51308      */
51309     maxRowsToMeasure : 0,
51310
51311     /**
51312      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51313      */
51314     trackMouseOver : true,
51315
51316     /**
51317     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51318     */
51319     
51320     /**
51321     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51322     */
51323     enableDragDrop : false,
51324     
51325     /**
51326     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51327     */
51328     enableColumnMove : true,
51329     
51330     /**
51331     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51332     */
51333     enableColumnHide : true,
51334     
51335     /**
51336     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51337     */
51338     enableRowHeightSync : false,
51339     
51340     /**
51341     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51342     */
51343     stripeRows : true,
51344     
51345     /**
51346     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51347     */
51348     autoHeight : false,
51349
51350     /**
51351      * @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.
51352      */
51353     autoExpandColumn : false,
51354
51355     /**
51356     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51357     * Default is 50.
51358     */
51359     autoExpandMin : 50,
51360
51361     /**
51362     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51363     */
51364     autoExpandMax : 1000,
51365
51366     /**
51367     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51368     */
51369     view : null,
51370
51371     /**
51372     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51373     */
51374     loadMask : false,
51375     /**
51376     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51377     */
51378     dropTarget: false,
51379     
51380    
51381     
51382     // private
51383     rendered : false,
51384
51385     /**
51386     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51387     * of a fixed width. Default is false.
51388     */
51389     /**
51390     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51391     */
51392     /**
51393      * Called once after all setup has been completed and the grid is ready to be rendered.
51394      * @return {Roo.grid.Grid} this
51395      */
51396     render : function()
51397     {
51398         var c = this.container;
51399         // try to detect autoHeight/width mode
51400         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51401             this.autoHeight = true;
51402         }
51403         var view = this.getView();
51404         view.init(this);
51405
51406         c.on("click", this.onClick, this);
51407         c.on("dblclick", this.onDblClick, this);
51408         c.on("contextmenu", this.onContextMenu, this);
51409         c.on("keydown", this.onKeyDown, this);
51410         if (Roo.isTouch) {
51411             c.on("touchstart", this.onTouchStart, this);
51412         }
51413
51414         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51415
51416         this.getSelectionModel().init(this);
51417
51418         view.render();
51419
51420         if(this.loadMask){
51421             this.loadMask = new Roo.LoadMask(this.container,
51422                     Roo.apply({store:this.dataSource}, this.loadMask));
51423         }
51424         
51425         
51426         if (this.toolbar && this.toolbar.xtype) {
51427             this.toolbar.container = this.getView().getHeaderPanel(true);
51428             this.toolbar = new Roo.Toolbar(this.toolbar);
51429         }
51430         if (this.footer && this.footer.xtype) {
51431             this.footer.dataSource = this.getDataSource();
51432             this.footer.container = this.getView().getFooterPanel(true);
51433             this.footer = Roo.factory(this.footer, Roo);
51434         }
51435         if (this.dropTarget && this.dropTarget.xtype) {
51436             delete this.dropTarget.xtype;
51437             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51438         }
51439         
51440         
51441         this.rendered = true;
51442         this.fireEvent('render', this);
51443         return this;
51444     },
51445
51446         /**
51447          * Reconfigures the grid to use a different Store and Column Model.
51448          * The View will be bound to the new objects and refreshed.
51449          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51450          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51451          */
51452     reconfigure : function(dataSource, colModel){
51453         if(this.loadMask){
51454             this.loadMask.destroy();
51455             this.loadMask = new Roo.LoadMask(this.container,
51456                     Roo.apply({store:dataSource}, this.loadMask));
51457         }
51458         this.view.bind(dataSource, colModel);
51459         this.dataSource = dataSource;
51460         this.colModel = colModel;
51461         this.view.refresh(true);
51462     },
51463
51464     // private
51465     onKeyDown : function(e){
51466         this.fireEvent("keydown", e);
51467     },
51468
51469     /**
51470      * Destroy this grid.
51471      * @param {Boolean} removeEl True to remove the element
51472      */
51473     destroy : function(removeEl, keepListeners){
51474         if(this.loadMask){
51475             this.loadMask.destroy();
51476         }
51477         var c = this.container;
51478         c.removeAllListeners();
51479         this.view.destroy();
51480         this.colModel.purgeListeners();
51481         if(!keepListeners){
51482             this.purgeListeners();
51483         }
51484         c.update("");
51485         if(removeEl === true){
51486             c.remove();
51487         }
51488     },
51489
51490     // private
51491     processEvent : function(name, e){
51492         // does this fire select???
51493         Roo.log('grid:processEvent '  + name);
51494         
51495         if (name != 'touchstart' ) {
51496             this.fireEvent(name, e);    
51497         }
51498         
51499         var t = e.getTarget();
51500         var v = this.view;
51501         var header = v.findHeaderIndex(t);
51502         if(header !== false){
51503             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
51504         }else{
51505             var row = v.findRowIndex(t);
51506             var cell = v.findCellIndex(t);
51507             if (name == 'touchstart') {
51508                 // first touch is always a click.
51509                 // hopefull this happens after selection is updated.?
51510                 name = false;
51511                 
51512                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51513                     var cs = this.selModel.getSelectedCell();
51514                     if (row == cs[0] && cell == cs[1]){
51515                         name = 'dblclick';
51516                     }
51517                 }
51518                 if (typeof(this.selModel.getSelections) != 'undefined') {
51519                     var cs = this.selModel.getSelections();
51520                     var ds = this.dataSource;
51521                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51522                         name = 'dblclick';
51523                     }
51524                 }
51525                 if (!name) {
51526                     return;
51527                 }
51528             }
51529             
51530             
51531             if(row !== false){
51532                 this.fireEvent("row" + name, this, row, e);
51533                 if(cell !== false){
51534                     this.fireEvent("cell" + name, this, row, cell, e);
51535                 }
51536             }
51537         }
51538     },
51539
51540     // private
51541     onClick : function(e){
51542         this.processEvent("click", e);
51543     },
51544    // private
51545     onTouchStart : function(e){
51546         this.processEvent("touchstart", e);
51547     },
51548
51549     // private
51550     onContextMenu : function(e, t){
51551         this.processEvent("contextmenu", e);
51552     },
51553
51554     // private
51555     onDblClick : function(e){
51556         this.processEvent("dblclick", e);
51557     },
51558
51559     // private
51560     walkCells : function(row, col, step, fn, scope){
51561         var cm = this.colModel, clen = cm.getColumnCount();
51562         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51563         if(step < 0){
51564             if(col < 0){
51565                 row--;
51566                 first = false;
51567             }
51568             while(row >= 0){
51569                 if(!first){
51570                     col = clen-1;
51571                 }
51572                 first = false;
51573                 while(col >= 0){
51574                     if(fn.call(scope || this, row, col, cm) === true){
51575                         return [row, col];
51576                     }
51577                     col--;
51578                 }
51579                 row--;
51580             }
51581         } else {
51582             if(col >= clen){
51583                 row++;
51584                 first = false;
51585             }
51586             while(row < rlen){
51587                 if(!first){
51588                     col = 0;
51589                 }
51590                 first = false;
51591                 while(col < clen){
51592                     if(fn.call(scope || this, row, col, cm) === true){
51593                         return [row, col];
51594                     }
51595                     col++;
51596                 }
51597                 row++;
51598             }
51599         }
51600         return null;
51601     },
51602
51603     // private
51604     getSelections : function(){
51605         return this.selModel.getSelections();
51606     },
51607
51608     /**
51609      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51610      * but if manual update is required this method will initiate it.
51611      */
51612     autoSize : function(){
51613         if(this.rendered){
51614             this.view.layout();
51615             if(this.view.adjustForScroll){
51616                 this.view.adjustForScroll();
51617             }
51618         }
51619     },
51620
51621     /**
51622      * Returns the grid's underlying element.
51623      * @return {Element} The element
51624      */
51625     getGridEl : function(){
51626         return this.container;
51627     },
51628
51629     // private for compatibility, overridden by editor grid
51630     stopEditing : function(){},
51631
51632     /**
51633      * Returns the grid's SelectionModel.
51634      * @return {SelectionModel}
51635      */
51636     getSelectionModel : function(){
51637         if(!this.selModel){
51638             this.selModel = new Roo.grid.RowSelectionModel();
51639         }
51640         return this.selModel;
51641     },
51642
51643     /**
51644      * Returns the grid's DataSource.
51645      * @return {DataSource}
51646      */
51647     getDataSource : function(){
51648         return this.dataSource;
51649     },
51650
51651     /**
51652      * Returns the grid's ColumnModel.
51653      * @return {ColumnModel}
51654      */
51655     getColumnModel : function(){
51656         return this.colModel;
51657     },
51658
51659     /**
51660      * Returns the grid's GridView object.
51661      * @return {GridView}
51662      */
51663     getView : function(){
51664         if(!this.view){
51665             this.view = new Roo.grid.GridView(this.viewConfig);
51666         }
51667         return this.view;
51668     },
51669     /**
51670      * Called to get grid's drag proxy text, by default returns this.ddText.
51671      * @return {String}
51672      */
51673     getDragDropText : function(){
51674         var count = this.selModel.getCount();
51675         return String.format(this.ddText, count, count == 1 ? '' : 's');
51676     }
51677 });
51678 /**
51679  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51680  * %0 is replaced with the number of selected rows.
51681  * @type String
51682  */
51683 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51684  * Based on:
51685  * Ext JS Library 1.1.1
51686  * Copyright(c) 2006-2007, Ext JS, LLC.
51687  *
51688  * Originally Released Under LGPL - original licence link has changed is not relivant.
51689  *
51690  * Fork - LGPL
51691  * <script type="text/javascript">
51692  */
51693  
51694 Roo.grid.AbstractGridView = function(){
51695         this.grid = null;
51696         
51697         this.events = {
51698             "beforerowremoved" : true,
51699             "beforerowsinserted" : true,
51700             "beforerefresh" : true,
51701             "rowremoved" : true,
51702             "rowsinserted" : true,
51703             "rowupdated" : true,
51704             "refresh" : true
51705         };
51706     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51707 };
51708
51709 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51710     rowClass : "x-grid-row",
51711     cellClass : "x-grid-cell",
51712     tdClass : "x-grid-td",
51713     hdClass : "x-grid-hd",
51714     splitClass : "x-grid-hd-split",
51715     
51716         init: function(grid){
51717         this.grid = grid;
51718                 var cid = this.grid.getGridEl().id;
51719         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51720         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51721         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51722         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51723         },
51724         
51725         getColumnRenderers : function(){
51726         var renderers = [];
51727         var cm = this.grid.colModel;
51728         var colCount = cm.getColumnCount();
51729         for(var i = 0; i < colCount; i++){
51730             renderers[i] = cm.getRenderer(i);
51731         }
51732         return renderers;
51733     },
51734     
51735     getColumnIds : function(){
51736         var ids = [];
51737         var cm = this.grid.colModel;
51738         var colCount = cm.getColumnCount();
51739         for(var i = 0; i < colCount; i++){
51740             ids[i] = cm.getColumnId(i);
51741         }
51742         return ids;
51743     },
51744     
51745     getDataIndexes : function(){
51746         if(!this.indexMap){
51747             this.indexMap = this.buildIndexMap();
51748         }
51749         return this.indexMap.colToData;
51750     },
51751     
51752     getColumnIndexByDataIndex : function(dataIndex){
51753         if(!this.indexMap){
51754             this.indexMap = this.buildIndexMap();
51755         }
51756         return this.indexMap.dataToCol[dataIndex];
51757     },
51758     
51759     /**
51760      * Set a css style for a column dynamically. 
51761      * @param {Number} colIndex The index of the column
51762      * @param {String} name The css property name
51763      * @param {String} value The css value
51764      */
51765     setCSSStyle : function(colIndex, name, value){
51766         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51767         Roo.util.CSS.updateRule(selector, name, value);
51768     },
51769     
51770     generateRules : function(cm){
51771         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51772         Roo.util.CSS.removeStyleSheet(rulesId);
51773         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51774             var cid = cm.getColumnId(i);
51775             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51776                          this.tdSelector, cid, " {\n}\n",
51777                          this.hdSelector, cid, " {\n}\n",
51778                          this.splitSelector, cid, " {\n}\n");
51779         }
51780         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51781     }
51782 });/*
51783  * Based on:
51784  * Ext JS Library 1.1.1
51785  * Copyright(c) 2006-2007, Ext JS, LLC.
51786  *
51787  * Originally Released Under LGPL - original licence link has changed is not relivant.
51788  *
51789  * Fork - LGPL
51790  * <script type="text/javascript">
51791  */
51792
51793 // private
51794 // This is a support class used internally by the Grid components
51795 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51796     this.grid = grid;
51797     this.view = grid.getView();
51798     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51799     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51800     if(hd2){
51801         this.setHandleElId(Roo.id(hd));
51802         this.setOuterHandleElId(Roo.id(hd2));
51803     }
51804     this.scroll = false;
51805 };
51806 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51807     maxDragWidth: 120,
51808     getDragData : function(e){
51809         var t = Roo.lib.Event.getTarget(e);
51810         var h = this.view.findHeaderCell(t);
51811         if(h){
51812             return {ddel: h.firstChild, header:h};
51813         }
51814         return false;
51815     },
51816
51817     onInitDrag : function(e){
51818         this.view.headersDisabled = true;
51819         var clone = this.dragData.ddel.cloneNode(true);
51820         clone.id = Roo.id();
51821         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51822         this.proxy.update(clone);
51823         return true;
51824     },
51825
51826     afterValidDrop : function(){
51827         var v = this.view;
51828         setTimeout(function(){
51829             v.headersDisabled = false;
51830         }, 50);
51831     },
51832
51833     afterInvalidDrop : function(){
51834         var v = this.view;
51835         setTimeout(function(){
51836             v.headersDisabled = false;
51837         }, 50);
51838     }
51839 });
51840 /*
51841  * Based on:
51842  * Ext JS Library 1.1.1
51843  * Copyright(c) 2006-2007, Ext JS, LLC.
51844  *
51845  * Originally Released Under LGPL - original licence link has changed is not relivant.
51846  *
51847  * Fork - LGPL
51848  * <script type="text/javascript">
51849  */
51850 // private
51851 // This is a support class used internally by the Grid components
51852 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51853     this.grid = grid;
51854     this.view = grid.getView();
51855     // split the proxies so they don't interfere with mouse events
51856     this.proxyTop = Roo.DomHelper.append(document.body, {
51857         cls:"col-move-top", html:"&#160;"
51858     }, true);
51859     this.proxyBottom = Roo.DomHelper.append(document.body, {
51860         cls:"col-move-bottom", html:"&#160;"
51861     }, true);
51862     this.proxyTop.hide = this.proxyBottom.hide = function(){
51863         this.setLeftTop(-100,-100);
51864         this.setStyle("visibility", "hidden");
51865     };
51866     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51867     // temporarily disabled
51868     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51869     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51870 };
51871 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51872     proxyOffsets : [-4, -9],
51873     fly: Roo.Element.fly,
51874
51875     getTargetFromEvent : function(e){
51876         var t = Roo.lib.Event.getTarget(e);
51877         var cindex = this.view.findCellIndex(t);
51878         if(cindex !== false){
51879             return this.view.getHeaderCell(cindex);
51880         }
51881         return null;
51882     },
51883
51884     nextVisible : function(h){
51885         var v = this.view, cm = this.grid.colModel;
51886         h = h.nextSibling;
51887         while(h){
51888             if(!cm.isHidden(v.getCellIndex(h))){
51889                 return h;
51890             }
51891             h = h.nextSibling;
51892         }
51893         return null;
51894     },
51895
51896     prevVisible : function(h){
51897         var v = this.view, cm = this.grid.colModel;
51898         h = h.prevSibling;
51899         while(h){
51900             if(!cm.isHidden(v.getCellIndex(h))){
51901                 return h;
51902             }
51903             h = h.prevSibling;
51904         }
51905         return null;
51906     },
51907
51908     positionIndicator : function(h, n, e){
51909         var x = Roo.lib.Event.getPageX(e);
51910         var r = Roo.lib.Dom.getRegion(n.firstChild);
51911         var px, pt, py = r.top + this.proxyOffsets[1];
51912         if((r.right - x) <= (r.right-r.left)/2){
51913             px = r.right+this.view.borderWidth;
51914             pt = "after";
51915         }else{
51916             px = r.left;
51917             pt = "before";
51918         }
51919         var oldIndex = this.view.getCellIndex(h);
51920         var newIndex = this.view.getCellIndex(n);
51921
51922         if(this.grid.colModel.isFixed(newIndex)){
51923             return false;
51924         }
51925
51926         var locked = this.grid.colModel.isLocked(newIndex);
51927
51928         if(pt == "after"){
51929             newIndex++;
51930         }
51931         if(oldIndex < newIndex){
51932             newIndex--;
51933         }
51934         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51935             return false;
51936         }
51937         px +=  this.proxyOffsets[0];
51938         this.proxyTop.setLeftTop(px, py);
51939         this.proxyTop.show();
51940         if(!this.bottomOffset){
51941             this.bottomOffset = this.view.mainHd.getHeight();
51942         }
51943         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51944         this.proxyBottom.show();
51945         return pt;
51946     },
51947
51948     onNodeEnter : function(n, dd, e, data){
51949         if(data.header != n){
51950             this.positionIndicator(data.header, n, e);
51951         }
51952     },
51953
51954     onNodeOver : function(n, dd, e, data){
51955         var result = false;
51956         if(data.header != n){
51957             result = this.positionIndicator(data.header, n, e);
51958         }
51959         if(!result){
51960             this.proxyTop.hide();
51961             this.proxyBottom.hide();
51962         }
51963         return result ? this.dropAllowed : this.dropNotAllowed;
51964     },
51965
51966     onNodeOut : function(n, dd, e, data){
51967         this.proxyTop.hide();
51968         this.proxyBottom.hide();
51969     },
51970
51971     onNodeDrop : function(n, dd, e, data){
51972         var h = data.header;
51973         if(h != n){
51974             var cm = this.grid.colModel;
51975             var x = Roo.lib.Event.getPageX(e);
51976             var r = Roo.lib.Dom.getRegion(n.firstChild);
51977             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
51978             var oldIndex = this.view.getCellIndex(h);
51979             var newIndex = this.view.getCellIndex(n);
51980             var locked = cm.isLocked(newIndex);
51981             if(pt == "after"){
51982                 newIndex++;
51983             }
51984             if(oldIndex < newIndex){
51985                 newIndex--;
51986             }
51987             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
51988                 return false;
51989             }
51990             cm.setLocked(oldIndex, locked, true);
51991             cm.moveColumn(oldIndex, newIndex);
51992             this.grid.fireEvent("columnmove", oldIndex, newIndex);
51993             return true;
51994         }
51995         return false;
51996     }
51997 });
51998 /*
51999  * Based on:
52000  * Ext JS Library 1.1.1
52001  * Copyright(c) 2006-2007, Ext JS, LLC.
52002  *
52003  * Originally Released Under LGPL - original licence link has changed is not relivant.
52004  *
52005  * Fork - LGPL
52006  * <script type="text/javascript">
52007  */
52008   
52009 /**
52010  * @class Roo.grid.GridView
52011  * @extends Roo.util.Observable
52012  *
52013  * @constructor
52014  * @param {Object} config
52015  */
52016 Roo.grid.GridView = function(config){
52017     Roo.grid.GridView.superclass.constructor.call(this);
52018     this.el = null;
52019
52020     Roo.apply(this, config);
52021 };
52022
52023 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52024
52025     unselectable :  'unselectable="on"',
52026     unselectableCls :  'x-unselectable',
52027     
52028     
52029     rowClass : "x-grid-row",
52030
52031     cellClass : "x-grid-col",
52032
52033     tdClass : "x-grid-td",
52034
52035     hdClass : "x-grid-hd",
52036
52037     splitClass : "x-grid-split",
52038
52039     sortClasses : ["sort-asc", "sort-desc"],
52040
52041     enableMoveAnim : false,
52042
52043     hlColor: "C3DAF9",
52044
52045     dh : Roo.DomHelper,
52046
52047     fly : Roo.Element.fly,
52048
52049     css : Roo.util.CSS,
52050
52051     borderWidth: 1,
52052
52053     splitOffset: 3,
52054
52055     scrollIncrement : 22,
52056
52057     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52058
52059     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52060
52061     bind : function(ds, cm){
52062         if(this.ds){
52063             this.ds.un("load", this.onLoad, this);
52064             this.ds.un("datachanged", this.onDataChange, this);
52065             this.ds.un("add", this.onAdd, this);
52066             this.ds.un("remove", this.onRemove, this);
52067             this.ds.un("update", this.onUpdate, this);
52068             this.ds.un("clear", this.onClear, this);
52069         }
52070         if(ds){
52071             ds.on("load", this.onLoad, this);
52072             ds.on("datachanged", this.onDataChange, this);
52073             ds.on("add", this.onAdd, this);
52074             ds.on("remove", this.onRemove, this);
52075             ds.on("update", this.onUpdate, this);
52076             ds.on("clear", this.onClear, this);
52077         }
52078         this.ds = ds;
52079
52080         if(this.cm){
52081             this.cm.un("widthchange", this.onColWidthChange, this);
52082             this.cm.un("headerchange", this.onHeaderChange, this);
52083             this.cm.un("hiddenchange", this.onHiddenChange, this);
52084             this.cm.un("columnmoved", this.onColumnMove, this);
52085             this.cm.un("columnlockchange", this.onColumnLock, this);
52086         }
52087         if(cm){
52088             this.generateRules(cm);
52089             cm.on("widthchange", this.onColWidthChange, this);
52090             cm.on("headerchange", this.onHeaderChange, this);
52091             cm.on("hiddenchange", this.onHiddenChange, this);
52092             cm.on("columnmoved", this.onColumnMove, this);
52093             cm.on("columnlockchange", this.onColumnLock, this);
52094         }
52095         this.cm = cm;
52096     },
52097
52098     init: function(grid){
52099         Roo.grid.GridView.superclass.init.call(this, grid);
52100
52101         this.bind(grid.dataSource, grid.colModel);
52102
52103         grid.on("headerclick", this.handleHeaderClick, this);
52104
52105         if(grid.trackMouseOver){
52106             grid.on("mouseover", this.onRowOver, this);
52107             grid.on("mouseout", this.onRowOut, this);
52108         }
52109         grid.cancelTextSelection = function(){};
52110         this.gridId = grid.id;
52111
52112         var tpls = this.templates || {};
52113
52114         if(!tpls.master){
52115             tpls.master = new Roo.Template(
52116                '<div class="x-grid" hidefocus="true">',
52117                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52118                   '<div class="x-grid-topbar"></div>',
52119                   '<div class="x-grid-scroller"><div></div></div>',
52120                   '<div class="x-grid-locked">',
52121                       '<div class="x-grid-header">{lockedHeader}</div>',
52122                       '<div class="x-grid-body">{lockedBody}</div>',
52123                   "</div>",
52124                   '<div class="x-grid-viewport">',
52125                       '<div class="x-grid-header">{header}</div>',
52126                       '<div class="x-grid-body">{body}</div>',
52127                   "</div>",
52128                   '<div class="x-grid-bottombar"></div>',
52129                  
52130                   '<div class="x-grid-resize-proxy">&#160;</div>',
52131                "</div>"
52132             );
52133             tpls.master.disableformats = true;
52134         }
52135
52136         if(!tpls.header){
52137             tpls.header = new Roo.Template(
52138                '<table border="0" cellspacing="0" cellpadding="0">',
52139                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52140                "</table>{splits}"
52141             );
52142             tpls.header.disableformats = true;
52143         }
52144         tpls.header.compile();
52145
52146         if(!tpls.hcell){
52147             tpls.hcell = new Roo.Template(
52148                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52149                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52150                 "</div></td>"
52151              );
52152              tpls.hcell.disableFormats = true;
52153         }
52154         tpls.hcell.compile();
52155
52156         if(!tpls.hsplit){
52157             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52158                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52159             tpls.hsplit.disableFormats = true;
52160         }
52161         tpls.hsplit.compile();
52162
52163         if(!tpls.body){
52164             tpls.body = new Roo.Template(
52165                '<table border="0" cellspacing="0" cellpadding="0">',
52166                "<tbody>{rows}</tbody>",
52167                "</table>"
52168             );
52169             tpls.body.disableFormats = true;
52170         }
52171         tpls.body.compile();
52172
52173         if(!tpls.row){
52174             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52175             tpls.row.disableFormats = true;
52176         }
52177         tpls.row.compile();
52178
52179         if(!tpls.cell){
52180             tpls.cell = new Roo.Template(
52181                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52182                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52183                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52184                 "</td>"
52185             );
52186             tpls.cell.disableFormats = true;
52187         }
52188         tpls.cell.compile();
52189
52190         this.templates = tpls;
52191     },
52192
52193     // remap these for backwards compat
52194     onColWidthChange : function(){
52195         this.updateColumns.apply(this, arguments);
52196     },
52197     onHeaderChange : function(){
52198         this.updateHeaders.apply(this, arguments);
52199     }, 
52200     onHiddenChange : function(){
52201         this.handleHiddenChange.apply(this, arguments);
52202     },
52203     onColumnMove : function(){
52204         this.handleColumnMove.apply(this, arguments);
52205     },
52206     onColumnLock : function(){
52207         this.handleLockChange.apply(this, arguments);
52208     },
52209
52210     onDataChange : function(){
52211         this.refresh();
52212         this.updateHeaderSortState();
52213     },
52214
52215     onClear : function(){
52216         this.refresh();
52217     },
52218
52219     onUpdate : function(ds, record){
52220         this.refreshRow(record);
52221     },
52222
52223     refreshRow : function(record){
52224         var ds = this.ds, index;
52225         if(typeof record == 'number'){
52226             index = record;
52227             record = ds.getAt(index);
52228         }else{
52229             index = ds.indexOf(record);
52230         }
52231         this.insertRows(ds, index, index, true);
52232         this.onRemove(ds, record, index+1, true);
52233         this.syncRowHeights(index, index);
52234         this.layout();
52235         this.fireEvent("rowupdated", this, index, record);
52236     },
52237
52238     onAdd : function(ds, records, index){
52239         this.insertRows(ds, index, index + (records.length-1));
52240     },
52241
52242     onRemove : function(ds, record, index, isUpdate){
52243         if(isUpdate !== true){
52244             this.fireEvent("beforerowremoved", this, index, record);
52245         }
52246         var bt = this.getBodyTable(), lt = this.getLockedTable();
52247         if(bt.rows[index]){
52248             bt.firstChild.removeChild(bt.rows[index]);
52249         }
52250         if(lt.rows[index]){
52251             lt.firstChild.removeChild(lt.rows[index]);
52252         }
52253         if(isUpdate !== true){
52254             this.stripeRows(index);
52255             this.syncRowHeights(index, index);
52256             this.layout();
52257             this.fireEvent("rowremoved", this, index, record);
52258         }
52259     },
52260
52261     onLoad : function(){
52262         this.scrollToTop();
52263     },
52264
52265     /**
52266      * Scrolls the grid to the top
52267      */
52268     scrollToTop : function(){
52269         if(this.scroller){
52270             this.scroller.dom.scrollTop = 0;
52271             this.syncScroll();
52272         }
52273     },
52274
52275     /**
52276      * Gets a panel in the header of the grid that can be used for toolbars etc.
52277      * After modifying the contents of this panel a call to grid.autoSize() may be
52278      * required to register any changes in size.
52279      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52280      * @return Roo.Element
52281      */
52282     getHeaderPanel : function(doShow){
52283         if(doShow){
52284             this.headerPanel.show();
52285         }
52286         return this.headerPanel;
52287     },
52288
52289     /**
52290      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52291      * After modifying the contents of this panel a call to grid.autoSize() may be
52292      * required to register any changes in size.
52293      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52294      * @return Roo.Element
52295      */
52296     getFooterPanel : function(doShow){
52297         if(doShow){
52298             this.footerPanel.show();
52299         }
52300         return this.footerPanel;
52301     },
52302
52303     initElements : function(){
52304         var E = Roo.Element;
52305         var el = this.grid.getGridEl().dom.firstChild;
52306         var cs = el.childNodes;
52307
52308         this.el = new E(el);
52309         
52310          this.focusEl = new E(el.firstChild);
52311         this.focusEl.swallowEvent("click", true);
52312         
52313         this.headerPanel = new E(cs[1]);
52314         this.headerPanel.enableDisplayMode("block");
52315
52316         this.scroller = new E(cs[2]);
52317         this.scrollSizer = new E(this.scroller.dom.firstChild);
52318
52319         this.lockedWrap = new E(cs[3]);
52320         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52321         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52322
52323         this.mainWrap = new E(cs[4]);
52324         this.mainHd = new E(this.mainWrap.dom.firstChild);
52325         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52326
52327         this.footerPanel = new E(cs[5]);
52328         this.footerPanel.enableDisplayMode("block");
52329
52330         this.resizeProxy = new E(cs[6]);
52331
52332         this.headerSelector = String.format(
52333            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52334            this.lockedHd.id, this.mainHd.id
52335         );
52336
52337         this.splitterSelector = String.format(
52338            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52339            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52340         );
52341     },
52342     idToCssName : function(s)
52343     {
52344         return s.replace(/[^a-z0-9]+/ig, '-');
52345     },
52346
52347     getHeaderCell : function(index){
52348         return Roo.DomQuery.select(this.headerSelector)[index];
52349     },
52350
52351     getHeaderCellMeasure : function(index){
52352         return this.getHeaderCell(index).firstChild;
52353     },
52354
52355     getHeaderCellText : function(index){
52356         return this.getHeaderCell(index).firstChild.firstChild;
52357     },
52358
52359     getLockedTable : function(){
52360         return this.lockedBody.dom.firstChild;
52361     },
52362
52363     getBodyTable : function(){
52364         return this.mainBody.dom.firstChild;
52365     },
52366
52367     getLockedRow : function(index){
52368         return this.getLockedTable().rows[index];
52369     },
52370
52371     getRow : function(index){
52372         return this.getBodyTable().rows[index];
52373     },
52374
52375     getRowComposite : function(index){
52376         if(!this.rowEl){
52377             this.rowEl = new Roo.CompositeElementLite();
52378         }
52379         var els = [], lrow, mrow;
52380         if(lrow = this.getLockedRow(index)){
52381             els.push(lrow);
52382         }
52383         if(mrow = this.getRow(index)){
52384             els.push(mrow);
52385         }
52386         this.rowEl.elements = els;
52387         return this.rowEl;
52388     },
52389     /**
52390      * Gets the 'td' of the cell
52391      * 
52392      * @param {Integer} rowIndex row to select
52393      * @param {Integer} colIndex column to select
52394      * 
52395      * @return {Object} 
52396      */
52397     getCell : function(rowIndex, colIndex){
52398         var locked = this.cm.getLockedCount();
52399         var source;
52400         if(colIndex < locked){
52401             source = this.lockedBody.dom.firstChild;
52402         }else{
52403             source = this.mainBody.dom.firstChild;
52404             colIndex -= locked;
52405         }
52406         return source.rows[rowIndex].childNodes[colIndex];
52407     },
52408
52409     getCellText : function(rowIndex, colIndex){
52410         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52411     },
52412
52413     getCellBox : function(cell){
52414         var b = this.fly(cell).getBox();
52415         if(Roo.isOpera){ // opera fails to report the Y
52416             b.y = cell.offsetTop + this.mainBody.getY();
52417         }
52418         return b;
52419     },
52420
52421     getCellIndex : function(cell){
52422         var id = String(cell.className).match(this.cellRE);
52423         if(id){
52424             return parseInt(id[1], 10);
52425         }
52426         return 0;
52427     },
52428
52429     findHeaderIndex : function(n){
52430         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52431         return r ? this.getCellIndex(r) : false;
52432     },
52433
52434     findHeaderCell : function(n){
52435         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52436         return r ? r : false;
52437     },
52438
52439     findRowIndex : function(n){
52440         if(!n){
52441             return false;
52442         }
52443         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52444         return r ? r.rowIndex : false;
52445     },
52446
52447     findCellIndex : function(node){
52448         var stop = this.el.dom;
52449         while(node && node != stop){
52450             if(this.findRE.test(node.className)){
52451                 return this.getCellIndex(node);
52452             }
52453             node = node.parentNode;
52454         }
52455         return false;
52456     },
52457
52458     getColumnId : function(index){
52459         return this.cm.getColumnId(index);
52460     },
52461
52462     getSplitters : function()
52463     {
52464         if(this.splitterSelector){
52465            return Roo.DomQuery.select(this.splitterSelector);
52466         }else{
52467             return null;
52468       }
52469     },
52470
52471     getSplitter : function(index){
52472         return this.getSplitters()[index];
52473     },
52474
52475     onRowOver : function(e, t){
52476         var row;
52477         if((row = this.findRowIndex(t)) !== false){
52478             this.getRowComposite(row).addClass("x-grid-row-over");
52479         }
52480     },
52481
52482     onRowOut : function(e, t){
52483         var row;
52484         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52485             this.getRowComposite(row).removeClass("x-grid-row-over");
52486         }
52487     },
52488
52489     renderHeaders : function(){
52490         var cm = this.cm;
52491         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52492         var cb = [], lb = [], sb = [], lsb = [], p = {};
52493         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52494             p.cellId = "x-grid-hd-0-" + i;
52495             p.splitId = "x-grid-csplit-0-" + i;
52496             p.id = cm.getColumnId(i);
52497             p.title = cm.getColumnTooltip(i) || "";
52498             p.value = cm.getColumnHeader(i) || "";
52499             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52500             if(!cm.isLocked(i)){
52501                 cb[cb.length] = ct.apply(p);
52502                 sb[sb.length] = st.apply(p);
52503             }else{
52504                 lb[lb.length] = ct.apply(p);
52505                 lsb[lsb.length] = st.apply(p);
52506             }
52507         }
52508         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52509                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52510     },
52511
52512     updateHeaders : function(){
52513         var html = this.renderHeaders();
52514         this.lockedHd.update(html[0]);
52515         this.mainHd.update(html[1]);
52516     },
52517
52518     /**
52519      * Focuses the specified row.
52520      * @param {Number} row The row index
52521      */
52522     focusRow : function(row)
52523     {
52524         //Roo.log('GridView.focusRow');
52525         var x = this.scroller.dom.scrollLeft;
52526         this.focusCell(row, 0, false);
52527         this.scroller.dom.scrollLeft = x;
52528     },
52529
52530     /**
52531      * Focuses the specified cell.
52532      * @param {Number} row The row index
52533      * @param {Number} col The column index
52534      * @param {Boolean} hscroll false to disable horizontal scrolling
52535      */
52536     focusCell : function(row, col, hscroll)
52537     {
52538         //Roo.log('GridView.focusCell');
52539         var el = this.ensureVisible(row, col, hscroll);
52540         this.focusEl.alignTo(el, "tl-tl");
52541         if(Roo.isGecko){
52542             this.focusEl.focus();
52543         }else{
52544             this.focusEl.focus.defer(1, this.focusEl);
52545         }
52546     },
52547
52548     /**
52549      * Scrolls the specified cell into view
52550      * @param {Number} row The row index
52551      * @param {Number} col The column index
52552      * @param {Boolean} hscroll false to disable horizontal scrolling
52553      */
52554     ensureVisible : function(row, col, hscroll)
52555     {
52556         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52557         //return null; //disable for testing.
52558         if(typeof row != "number"){
52559             row = row.rowIndex;
52560         }
52561         if(row < 0 && row >= this.ds.getCount()){
52562             return  null;
52563         }
52564         col = (col !== undefined ? col : 0);
52565         var cm = this.grid.colModel;
52566         while(cm.isHidden(col)){
52567             col++;
52568         }
52569
52570         var el = this.getCell(row, col);
52571         if(!el){
52572             return null;
52573         }
52574         var c = this.scroller.dom;
52575
52576         var ctop = parseInt(el.offsetTop, 10);
52577         var cleft = parseInt(el.offsetLeft, 10);
52578         var cbot = ctop + el.offsetHeight;
52579         var cright = cleft + el.offsetWidth;
52580         
52581         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52582         var stop = parseInt(c.scrollTop, 10);
52583         var sleft = parseInt(c.scrollLeft, 10);
52584         var sbot = stop + ch;
52585         var sright = sleft + c.clientWidth;
52586         /*
52587         Roo.log('GridView.ensureVisible:' +
52588                 ' ctop:' + ctop +
52589                 ' c.clientHeight:' + c.clientHeight +
52590                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52591                 ' stop:' + stop +
52592                 ' cbot:' + cbot +
52593                 ' sbot:' + sbot +
52594                 ' ch:' + ch  
52595                 );
52596         */
52597         if(ctop < stop){
52598              c.scrollTop = ctop;
52599             //Roo.log("set scrolltop to ctop DISABLE?");
52600         }else if(cbot > sbot){
52601             //Roo.log("set scrolltop to cbot-ch");
52602             c.scrollTop = cbot-ch;
52603         }
52604         
52605         if(hscroll !== false){
52606             if(cleft < sleft){
52607                 c.scrollLeft = cleft;
52608             }else if(cright > sright){
52609                 c.scrollLeft = cright-c.clientWidth;
52610             }
52611         }
52612          
52613         return el;
52614     },
52615
52616     updateColumns : function(){
52617         this.grid.stopEditing();
52618         var cm = this.grid.colModel, colIds = this.getColumnIds();
52619         //var totalWidth = cm.getTotalWidth();
52620         var pos = 0;
52621         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52622             //if(cm.isHidden(i)) continue;
52623             var w = cm.getColumnWidth(i);
52624             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52625             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52626         }
52627         this.updateSplitters();
52628     },
52629
52630     generateRules : function(cm){
52631         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52632         Roo.util.CSS.removeStyleSheet(rulesId);
52633         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52634             var cid = cm.getColumnId(i);
52635             var align = '';
52636             if(cm.config[i].align){
52637                 align = 'text-align:'+cm.config[i].align+';';
52638             }
52639             var hidden = '';
52640             if(cm.isHidden(i)){
52641                 hidden = 'display:none;';
52642             }
52643             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52644             ruleBuf.push(
52645                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52646                     this.hdSelector, cid, " {\n", align, width, "}\n",
52647                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52648                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52649         }
52650         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52651     },
52652
52653     updateSplitters : function(){
52654         var cm = this.cm, s = this.getSplitters();
52655         if(s){ // splitters not created yet
52656             var pos = 0, locked = true;
52657             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52658                 if(cm.isHidden(i)) continue;
52659                 var w = cm.getColumnWidth(i); // make sure it's a number
52660                 if(!cm.isLocked(i) && locked){
52661                     pos = 0;
52662                     locked = false;
52663                 }
52664                 pos += w;
52665                 s[i].style.left = (pos-this.splitOffset) + "px";
52666             }
52667         }
52668     },
52669
52670     handleHiddenChange : function(colModel, colIndex, hidden){
52671         if(hidden){
52672             this.hideColumn(colIndex);
52673         }else{
52674             this.unhideColumn(colIndex);
52675         }
52676     },
52677
52678     hideColumn : function(colIndex){
52679         var cid = this.getColumnId(colIndex);
52680         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52681         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52682         if(Roo.isSafari){
52683             this.updateHeaders();
52684         }
52685         this.updateSplitters();
52686         this.layout();
52687     },
52688
52689     unhideColumn : function(colIndex){
52690         var cid = this.getColumnId(colIndex);
52691         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52692         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52693
52694         if(Roo.isSafari){
52695             this.updateHeaders();
52696         }
52697         this.updateSplitters();
52698         this.layout();
52699     },
52700
52701     insertRows : function(dm, firstRow, lastRow, isUpdate){
52702         if(firstRow == 0 && lastRow == dm.getCount()-1){
52703             this.refresh();
52704         }else{
52705             if(!isUpdate){
52706                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52707             }
52708             var s = this.getScrollState();
52709             var markup = this.renderRows(firstRow, lastRow);
52710             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52711             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52712             this.restoreScroll(s);
52713             if(!isUpdate){
52714                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52715                 this.syncRowHeights(firstRow, lastRow);
52716                 this.stripeRows(firstRow);
52717                 this.layout();
52718             }
52719         }
52720     },
52721
52722     bufferRows : function(markup, target, index){
52723         var before = null, trows = target.rows, tbody = target.tBodies[0];
52724         if(index < trows.length){
52725             before = trows[index];
52726         }
52727         var b = document.createElement("div");
52728         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52729         var rows = b.firstChild.rows;
52730         for(var i = 0, len = rows.length; i < len; i++){
52731             if(before){
52732                 tbody.insertBefore(rows[0], before);
52733             }else{
52734                 tbody.appendChild(rows[0]);
52735             }
52736         }
52737         b.innerHTML = "";
52738         b = null;
52739     },
52740
52741     deleteRows : function(dm, firstRow, lastRow){
52742         if(dm.getRowCount()<1){
52743             this.fireEvent("beforerefresh", this);
52744             this.mainBody.update("");
52745             this.lockedBody.update("");
52746             this.fireEvent("refresh", this);
52747         }else{
52748             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52749             var bt = this.getBodyTable();
52750             var tbody = bt.firstChild;
52751             var rows = bt.rows;
52752             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52753                 tbody.removeChild(rows[firstRow]);
52754             }
52755             this.stripeRows(firstRow);
52756             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52757         }
52758     },
52759
52760     updateRows : function(dataSource, firstRow, lastRow){
52761         var s = this.getScrollState();
52762         this.refresh();
52763         this.restoreScroll(s);
52764     },
52765
52766     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52767         if(!noRefresh){
52768            this.refresh();
52769         }
52770         this.updateHeaderSortState();
52771     },
52772
52773     getScrollState : function(){
52774         
52775         var sb = this.scroller.dom;
52776         return {left: sb.scrollLeft, top: sb.scrollTop};
52777     },
52778
52779     stripeRows : function(startRow){
52780         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52781             return;
52782         }
52783         startRow = startRow || 0;
52784         var rows = this.getBodyTable().rows;
52785         var lrows = this.getLockedTable().rows;
52786         var cls = ' x-grid-row-alt ';
52787         for(var i = startRow, len = rows.length; i < len; i++){
52788             var row = rows[i], lrow = lrows[i];
52789             var isAlt = ((i+1) % 2 == 0);
52790             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52791             if(isAlt == hasAlt){
52792                 continue;
52793             }
52794             if(isAlt){
52795                 row.className += " x-grid-row-alt";
52796             }else{
52797                 row.className = row.className.replace("x-grid-row-alt", "");
52798             }
52799             if(lrow){
52800                 lrow.className = row.className;
52801             }
52802         }
52803     },
52804
52805     restoreScroll : function(state){
52806         //Roo.log('GridView.restoreScroll');
52807         var sb = this.scroller.dom;
52808         sb.scrollLeft = state.left;
52809         sb.scrollTop = state.top;
52810         this.syncScroll();
52811     },
52812
52813     syncScroll : function(){
52814         //Roo.log('GridView.syncScroll');
52815         var sb = this.scroller.dom;
52816         var sh = this.mainHd.dom;
52817         var bs = this.mainBody.dom;
52818         var lv = this.lockedBody.dom;
52819         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52820         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52821     },
52822
52823     handleScroll : function(e){
52824         this.syncScroll();
52825         var sb = this.scroller.dom;
52826         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52827         e.stopEvent();
52828     },
52829
52830     handleWheel : function(e){
52831         var d = e.getWheelDelta();
52832         this.scroller.dom.scrollTop -= d*22;
52833         // set this here to prevent jumpy scrolling on large tables
52834         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52835         e.stopEvent();
52836     },
52837
52838     renderRows : function(startRow, endRow){
52839         // pull in all the crap needed to render rows
52840         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52841         var colCount = cm.getColumnCount();
52842
52843         if(ds.getCount() < 1){
52844             return ["", ""];
52845         }
52846
52847         // build a map for all the columns
52848         var cs = [];
52849         for(var i = 0; i < colCount; i++){
52850             var name = cm.getDataIndex(i);
52851             cs[i] = {
52852                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52853                 renderer : cm.getRenderer(i),
52854                 id : cm.getColumnId(i),
52855                 locked : cm.isLocked(i)
52856             };
52857         }
52858
52859         startRow = startRow || 0;
52860         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52861
52862         // records to render
52863         var rs = ds.getRange(startRow, endRow);
52864
52865         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52866     },
52867
52868     // As much as I hate to duplicate code, this was branched because FireFox really hates
52869     // [].join("") on strings. The performance difference was substantial enough to
52870     // branch this function
52871     doRender : Roo.isGecko ?
52872             function(cs, rs, ds, startRow, colCount, stripe){
52873                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52874                 // buffers
52875                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52876                 
52877                 var hasListener = this.grid.hasListener('rowclass');
52878                 var rowcfg = {};
52879                 for(var j = 0, len = rs.length; j < len; j++){
52880                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52881                     for(var i = 0; i < colCount; i++){
52882                         c = cs[i];
52883                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52884                         p.id = c.id;
52885                         p.css = p.attr = "";
52886                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52887                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52888                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52889                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52890                         }
52891                         var markup = ct.apply(p);
52892                         if(!c.locked){
52893                             cb+= markup;
52894                         }else{
52895                             lcb+= markup;
52896                         }
52897                     }
52898                     var alt = [];
52899                     if(stripe && ((rowIndex+1) % 2 == 0)){
52900                         alt.push("x-grid-row-alt")
52901                     }
52902                     if(r.dirty){
52903                         alt.push(  " x-grid-dirty-row");
52904                     }
52905                     rp.cells = lcb;
52906                     if(this.getRowClass){
52907                         alt.push(this.getRowClass(r, rowIndex));
52908                     }
52909                     if (hasListener) {
52910                         rowcfg = {
52911                              
52912                             record: r,
52913                             rowIndex : rowIndex,
52914                             rowClass : ''
52915                         }
52916                         this.grid.fireEvent('rowclass', this, rowcfg);
52917                         alt.push(rowcfg.rowClass);
52918                     }
52919                     rp.alt = alt.join(" ");
52920                     lbuf+= rt.apply(rp);
52921                     rp.cells = cb;
52922                     buf+=  rt.apply(rp);
52923                 }
52924                 return [lbuf, buf];
52925             } :
52926             function(cs, rs, ds, startRow, colCount, stripe){
52927                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52928                 // buffers
52929                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52930                 var hasListener = this.grid.hasListener('rowclass');
52931  
52932                 var rowcfg = {};
52933                 for(var j = 0, len = rs.length; j < len; j++){
52934                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52935                     for(var i = 0; i < colCount; i++){
52936                         c = cs[i];
52937                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52938                         p.id = c.id;
52939                         p.css = p.attr = "";
52940                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52941                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52942                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52943                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52944                         }
52945                         
52946                         var markup = ct.apply(p);
52947                         if(!c.locked){
52948                             cb[cb.length] = markup;
52949                         }else{
52950                             lcb[lcb.length] = markup;
52951                         }
52952                     }
52953                     var alt = [];
52954                     if(stripe && ((rowIndex+1) % 2 == 0)){
52955                         alt.push( "x-grid-row-alt");
52956                     }
52957                     if(r.dirty){
52958                         alt.push(" x-grid-dirty-row");
52959                     }
52960                     rp.cells = lcb;
52961                     if(this.getRowClass){
52962                         alt.push( this.getRowClass(r, rowIndex));
52963                     }
52964                     if (hasListener) {
52965                         rowcfg = {
52966                              
52967                             record: r,
52968                             rowIndex : rowIndex,
52969                             rowClass : ''
52970                         }
52971                         this.grid.fireEvent('rowclass', this, rowcfg);
52972                         alt.push(rowcfg.rowClass);
52973                     }
52974                     rp.alt = alt.join(" ");
52975                     rp.cells = lcb.join("");
52976                     lbuf[lbuf.length] = rt.apply(rp);
52977                     rp.cells = cb.join("");
52978                     buf[buf.length] =  rt.apply(rp);
52979                 }
52980                 return [lbuf.join(""), buf.join("")];
52981             },
52982
52983     renderBody : function(){
52984         var markup = this.renderRows();
52985         var bt = this.templates.body;
52986         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
52987     },
52988
52989     /**
52990      * Refreshes the grid
52991      * @param {Boolean} headersToo
52992      */
52993     refresh : function(headersToo){
52994         this.fireEvent("beforerefresh", this);
52995         this.grid.stopEditing();
52996         var result = this.renderBody();
52997         this.lockedBody.update(result[0]);
52998         this.mainBody.update(result[1]);
52999         if(headersToo === true){
53000             this.updateHeaders();
53001             this.updateColumns();
53002             this.updateSplitters();
53003             this.updateHeaderSortState();
53004         }
53005         this.syncRowHeights();
53006         this.layout();
53007         this.fireEvent("refresh", this);
53008     },
53009
53010     handleColumnMove : function(cm, oldIndex, newIndex){
53011         this.indexMap = null;
53012         var s = this.getScrollState();
53013         this.refresh(true);
53014         this.restoreScroll(s);
53015         this.afterMove(newIndex);
53016     },
53017
53018     afterMove : function(colIndex){
53019         if(this.enableMoveAnim && Roo.enableFx){
53020             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53021         }
53022         // if multisort - fix sortOrder, and reload..
53023         if (this.grid.dataSource.multiSort) {
53024             // the we can call sort again..
53025             var dm = this.grid.dataSource;
53026             var cm = this.grid.colModel;
53027             var so = [];
53028             for(var i = 0; i < cm.config.length; i++ ) {
53029                 
53030                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53031                     continue; // dont' bother, it's not in sort list or being set.
53032                 }
53033                 
53034                 so.push(cm.config[i].dataIndex);
53035             };
53036             dm.sortOrder = so;
53037             dm.load(dm.lastOptions);
53038             
53039             
53040         }
53041         
53042     },
53043
53044     updateCell : function(dm, rowIndex, dataIndex){
53045         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53046         if(typeof colIndex == "undefined"){ // not present in grid
53047             return;
53048         }
53049         var cm = this.grid.colModel;
53050         var cell = this.getCell(rowIndex, colIndex);
53051         var cellText = this.getCellText(rowIndex, colIndex);
53052
53053         var p = {
53054             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53055             id : cm.getColumnId(colIndex),
53056             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53057         };
53058         var renderer = cm.getRenderer(colIndex);
53059         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53060         if(typeof val == "undefined" || val === "") val = "&#160;";
53061         cellText.innerHTML = val;
53062         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53063         this.syncRowHeights(rowIndex, rowIndex);
53064     },
53065
53066     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53067         var maxWidth = 0;
53068         if(this.grid.autoSizeHeaders){
53069             var h = this.getHeaderCellMeasure(colIndex);
53070             maxWidth = Math.max(maxWidth, h.scrollWidth);
53071         }
53072         var tb, index;
53073         if(this.cm.isLocked(colIndex)){
53074             tb = this.getLockedTable();
53075             index = colIndex;
53076         }else{
53077             tb = this.getBodyTable();
53078             index = colIndex - this.cm.getLockedCount();
53079         }
53080         if(tb && tb.rows){
53081             var rows = tb.rows;
53082             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53083             for(var i = 0; i < stopIndex; i++){
53084                 var cell = rows[i].childNodes[index].firstChild;
53085                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53086             }
53087         }
53088         return maxWidth + /*margin for error in IE*/ 5;
53089     },
53090     /**
53091      * Autofit a column to its content.
53092      * @param {Number} colIndex
53093      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53094      */
53095      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53096          if(this.cm.isHidden(colIndex)){
53097              return; // can't calc a hidden column
53098          }
53099         if(forceMinSize){
53100             var cid = this.cm.getColumnId(colIndex);
53101             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53102            if(this.grid.autoSizeHeaders){
53103                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53104            }
53105         }
53106         var newWidth = this.calcColumnWidth(colIndex);
53107         this.cm.setColumnWidth(colIndex,
53108             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53109         if(!suppressEvent){
53110             this.grid.fireEvent("columnresize", colIndex, newWidth);
53111         }
53112     },
53113
53114     /**
53115      * Autofits all columns to their content and then expands to fit any extra space in the grid
53116      */
53117      autoSizeColumns : function(){
53118         var cm = this.grid.colModel;
53119         var colCount = cm.getColumnCount();
53120         for(var i = 0; i < colCount; i++){
53121             this.autoSizeColumn(i, true, true);
53122         }
53123         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53124             this.fitColumns();
53125         }else{
53126             this.updateColumns();
53127             this.layout();
53128         }
53129     },
53130
53131     /**
53132      * Autofits all columns to the grid's width proportionate with their current size
53133      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53134      */
53135     fitColumns : function(reserveScrollSpace){
53136         var cm = this.grid.colModel;
53137         var colCount = cm.getColumnCount();
53138         var cols = [];
53139         var width = 0;
53140         var i, w;
53141         for (i = 0; i < colCount; i++){
53142             if(!cm.isHidden(i) && !cm.isFixed(i)){
53143                 w = cm.getColumnWidth(i);
53144                 cols.push(i);
53145                 cols.push(w);
53146                 width += w;
53147             }
53148         }
53149         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53150         if(reserveScrollSpace){
53151             avail -= 17;
53152         }
53153         var frac = (avail - cm.getTotalWidth())/width;
53154         while (cols.length){
53155             w = cols.pop();
53156             i = cols.pop();
53157             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53158         }
53159         this.updateColumns();
53160         this.layout();
53161     },
53162
53163     onRowSelect : function(rowIndex){
53164         var row = this.getRowComposite(rowIndex);
53165         row.addClass("x-grid-row-selected");
53166     },
53167
53168     onRowDeselect : function(rowIndex){
53169         var row = this.getRowComposite(rowIndex);
53170         row.removeClass("x-grid-row-selected");
53171     },
53172
53173     onCellSelect : function(row, col){
53174         var cell = this.getCell(row, col);
53175         if(cell){
53176             Roo.fly(cell).addClass("x-grid-cell-selected");
53177         }
53178     },
53179
53180     onCellDeselect : function(row, col){
53181         var cell = this.getCell(row, col);
53182         if(cell){
53183             Roo.fly(cell).removeClass("x-grid-cell-selected");
53184         }
53185     },
53186
53187     updateHeaderSortState : function(){
53188         
53189         // sort state can be single { field: xxx, direction : yyy}
53190         // or   { xxx=>ASC , yyy : DESC ..... }
53191         
53192         var mstate = {};
53193         if (!this.ds.multiSort) { 
53194             var state = this.ds.getSortState();
53195             if(!state){
53196                 return;
53197             }
53198             mstate[state.field] = state.direction;
53199             // FIXME... - this is not used here.. but might be elsewhere..
53200             this.sortState = state;
53201             
53202         } else {
53203             mstate = this.ds.sortToggle;
53204         }
53205         //remove existing sort classes..
53206         
53207         var sc = this.sortClasses;
53208         var hds = this.el.select(this.headerSelector).removeClass(sc);
53209         
53210         for(var f in mstate) {
53211         
53212             var sortColumn = this.cm.findColumnIndex(f);
53213             
53214             if(sortColumn != -1){
53215                 var sortDir = mstate[f];        
53216                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53217             }
53218         }
53219         
53220          
53221         
53222     },
53223
53224
53225     handleHeaderClick : function(g, index){
53226         if(this.headersDisabled){
53227             return;
53228         }
53229         var dm = g.dataSource, cm = g.colModel;
53230         if(!cm.isSortable(index)){
53231             return;
53232         }
53233         g.stopEditing();
53234         
53235         if (dm.multiSort) {
53236             // update the sortOrder
53237             var so = [];
53238             for(var i = 0; i < cm.config.length; i++ ) {
53239                 
53240                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53241                     continue; // dont' bother, it's not in sort list or being set.
53242                 }
53243                 
53244                 so.push(cm.config[i].dataIndex);
53245             };
53246             dm.sortOrder = so;
53247         }
53248         
53249         
53250         dm.sort(cm.getDataIndex(index));
53251     },
53252
53253
53254     destroy : function(){
53255         if(this.colMenu){
53256             this.colMenu.removeAll();
53257             Roo.menu.MenuMgr.unregister(this.colMenu);
53258             this.colMenu.getEl().remove();
53259             delete this.colMenu;
53260         }
53261         if(this.hmenu){
53262             this.hmenu.removeAll();
53263             Roo.menu.MenuMgr.unregister(this.hmenu);
53264             this.hmenu.getEl().remove();
53265             delete this.hmenu;
53266         }
53267         if(this.grid.enableColumnMove){
53268             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53269             if(dds){
53270                 for(var dd in dds){
53271                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53272                         var elid = dds[dd].dragElId;
53273                         dds[dd].unreg();
53274                         Roo.get(elid).remove();
53275                     } else if(dds[dd].config.isTarget){
53276                         dds[dd].proxyTop.remove();
53277                         dds[dd].proxyBottom.remove();
53278                         dds[dd].unreg();
53279                     }
53280                     if(Roo.dd.DDM.locationCache[dd]){
53281                         delete Roo.dd.DDM.locationCache[dd];
53282                     }
53283                 }
53284                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53285             }
53286         }
53287         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53288         this.bind(null, null);
53289         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53290     },
53291
53292     handleLockChange : function(){
53293         this.refresh(true);
53294     },
53295
53296     onDenyColumnLock : function(){
53297
53298     },
53299
53300     onDenyColumnHide : function(){
53301
53302     },
53303
53304     handleHdMenuClick : function(item){
53305         var index = this.hdCtxIndex;
53306         var cm = this.cm, ds = this.ds;
53307         switch(item.id){
53308             case "asc":
53309                 ds.sort(cm.getDataIndex(index), "ASC");
53310                 break;
53311             case "desc":
53312                 ds.sort(cm.getDataIndex(index), "DESC");
53313                 break;
53314             case "lock":
53315                 var lc = cm.getLockedCount();
53316                 if(cm.getColumnCount(true) <= lc+1){
53317                     this.onDenyColumnLock();
53318                     return;
53319                 }
53320                 if(lc != index){
53321                     cm.setLocked(index, true, true);
53322                     cm.moveColumn(index, lc);
53323                     this.grid.fireEvent("columnmove", index, lc);
53324                 }else{
53325                     cm.setLocked(index, true);
53326                 }
53327             break;
53328             case "unlock":
53329                 var lc = cm.getLockedCount();
53330                 if((lc-1) != index){
53331                     cm.setLocked(index, false, true);
53332                     cm.moveColumn(index, lc-1);
53333                     this.grid.fireEvent("columnmove", index, lc-1);
53334                 }else{
53335                     cm.setLocked(index, false);
53336                 }
53337             break;
53338             default:
53339                 index = cm.getIndexById(item.id.substr(4));
53340                 if(index != -1){
53341                     if(item.checked && cm.getColumnCount(true) <= 1){
53342                         this.onDenyColumnHide();
53343                         return false;
53344                     }
53345                     cm.setHidden(index, item.checked);
53346                 }
53347         }
53348         return true;
53349     },
53350
53351     beforeColMenuShow : function(){
53352         var cm = this.cm,  colCount = cm.getColumnCount();
53353         this.colMenu.removeAll();
53354         for(var i = 0; i < colCount; i++){
53355             this.colMenu.add(new Roo.menu.CheckItem({
53356                 id: "col-"+cm.getColumnId(i),
53357                 text: cm.getColumnHeader(i),
53358                 checked: !cm.isHidden(i),
53359                 hideOnClick:false
53360             }));
53361         }
53362     },
53363
53364     handleHdCtx : function(g, index, e){
53365         e.stopEvent();
53366         var hd = this.getHeaderCell(index);
53367         this.hdCtxIndex = index;
53368         var ms = this.hmenu.items, cm = this.cm;
53369         ms.get("asc").setDisabled(!cm.isSortable(index));
53370         ms.get("desc").setDisabled(!cm.isSortable(index));
53371         if(this.grid.enableColLock !== false){
53372             ms.get("lock").setDisabled(cm.isLocked(index));
53373             ms.get("unlock").setDisabled(!cm.isLocked(index));
53374         }
53375         this.hmenu.show(hd, "tl-bl");
53376     },
53377
53378     handleHdOver : function(e){
53379         var hd = this.findHeaderCell(e.getTarget());
53380         if(hd && !this.headersDisabled){
53381             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53382                this.fly(hd).addClass("x-grid-hd-over");
53383             }
53384         }
53385     },
53386
53387     handleHdOut : function(e){
53388         var hd = this.findHeaderCell(e.getTarget());
53389         if(hd){
53390             this.fly(hd).removeClass("x-grid-hd-over");
53391         }
53392     },
53393
53394     handleSplitDblClick : function(e, t){
53395         var i = this.getCellIndex(t);
53396         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53397             this.autoSizeColumn(i, true);
53398             this.layout();
53399         }
53400     },
53401
53402     render : function(){
53403
53404         var cm = this.cm;
53405         var colCount = cm.getColumnCount();
53406
53407         if(this.grid.monitorWindowResize === true){
53408             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53409         }
53410         var header = this.renderHeaders();
53411         var body = this.templates.body.apply({rows:""});
53412         var html = this.templates.master.apply({
53413             lockedBody: body,
53414             body: body,
53415             lockedHeader: header[0],
53416             header: header[1]
53417         });
53418
53419         //this.updateColumns();
53420
53421         this.grid.getGridEl().dom.innerHTML = html;
53422
53423         this.initElements();
53424         
53425         // a kludge to fix the random scolling effect in webkit
53426         this.el.on("scroll", function() {
53427             this.el.dom.scrollTop=0; // hopefully not recursive..
53428         },this);
53429
53430         this.scroller.on("scroll", this.handleScroll, this);
53431         this.lockedBody.on("mousewheel", this.handleWheel, this);
53432         this.mainBody.on("mousewheel", this.handleWheel, this);
53433
53434         this.mainHd.on("mouseover", this.handleHdOver, this);
53435         this.mainHd.on("mouseout", this.handleHdOut, this);
53436         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53437                 {delegate: "."+this.splitClass});
53438
53439         this.lockedHd.on("mouseover", this.handleHdOver, this);
53440         this.lockedHd.on("mouseout", this.handleHdOut, this);
53441         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53442                 {delegate: "."+this.splitClass});
53443
53444         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53445             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53446         }
53447
53448         this.updateSplitters();
53449
53450         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53451             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53452             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53453         }
53454
53455         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53456             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53457             this.hmenu.add(
53458                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53459                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53460             );
53461             if(this.grid.enableColLock !== false){
53462                 this.hmenu.add('-',
53463                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53464                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53465                 );
53466             }
53467             if(this.grid.enableColumnHide !== false){
53468
53469                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53470                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53471                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53472
53473                 this.hmenu.add('-',
53474                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53475                 );
53476             }
53477             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53478
53479             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53480         }
53481
53482         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53483             this.dd = new Roo.grid.GridDragZone(this.grid, {
53484                 ddGroup : this.grid.ddGroup || 'GridDD'
53485             });
53486             
53487         }
53488
53489         /*
53490         for(var i = 0; i < colCount; i++){
53491             if(cm.isHidden(i)){
53492                 this.hideColumn(i);
53493             }
53494             if(cm.config[i].align){
53495                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53496                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53497             }
53498         }*/
53499         
53500         this.updateHeaderSortState();
53501
53502         this.beforeInitialResize();
53503         this.layout(true);
53504
53505         // two part rendering gives faster view to the user
53506         this.renderPhase2.defer(1, this);
53507     },
53508
53509     renderPhase2 : function(){
53510         // render the rows now
53511         this.refresh();
53512         if(this.grid.autoSizeColumns){
53513             this.autoSizeColumns();
53514         }
53515     },
53516
53517     beforeInitialResize : function(){
53518
53519     },
53520
53521     onColumnSplitterMoved : function(i, w){
53522         this.userResized = true;
53523         var cm = this.grid.colModel;
53524         cm.setColumnWidth(i, w, true);
53525         var cid = cm.getColumnId(i);
53526         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53527         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53528         this.updateSplitters();
53529         this.layout();
53530         this.grid.fireEvent("columnresize", i, w);
53531     },
53532
53533     syncRowHeights : function(startIndex, endIndex){
53534         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53535             startIndex = startIndex || 0;
53536             var mrows = this.getBodyTable().rows;
53537             var lrows = this.getLockedTable().rows;
53538             var len = mrows.length-1;
53539             endIndex = Math.min(endIndex || len, len);
53540             for(var i = startIndex; i <= endIndex; i++){
53541                 var m = mrows[i], l = lrows[i];
53542                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53543                 m.style.height = l.style.height = h + "px";
53544             }
53545         }
53546     },
53547
53548     layout : function(initialRender, is2ndPass){
53549         var g = this.grid;
53550         var auto = g.autoHeight;
53551         var scrollOffset = 16;
53552         var c = g.getGridEl(), cm = this.cm,
53553                 expandCol = g.autoExpandColumn,
53554                 gv = this;
53555         //c.beginMeasure();
53556
53557         if(!c.dom.offsetWidth){ // display:none?
53558             if(initialRender){
53559                 this.lockedWrap.show();
53560                 this.mainWrap.show();
53561             }
53562             return;
53563         }
53564
53565         var hasLock = this.cm.isLocked(0);
53566
53567         var tbh = this.headerPanel.getHeight();
53568         var bbh = this.footerPanel.getHeight();
53569
53570         if(auto){
53571             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53572             var newHeight = ch + c.getBorderWidth("tb");
53573             if(g.maxHeight){
53574                 newHeight = Math.min(g.maxHeight, newHeight);
53575             }
53576             c.setHeight(newHeight);
53577         }
53578
53579         if(g.autoWidth){
53580             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53581         }
53582
53583         var s = this.scroller;
53584
53585         var csize = c.getSize(true);
53586
53587         this.el.setSize(csize.width, csize.height);
53588
53589         this.headerPanel.setWidth(csize.width);
53590         this.footerPanel.setWidth(csize.width);
53591
53592         var hdHeight = this.mainHd.getHeight();
53593         var vw = csize.width;
53594         var vh = csize.height - (tbh + bbh);
53595
53596         s.setSize(vw, vh);
53597
53598         var bt = this.getBodyTable();
53599         var ltWidth = hasLock ?
53600                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
53601
53602         var scrollHeight = bt.offsetHeight;
53603         var scrollWidth = ltWidth + bt.offsetWidth;
53604         var vscroll = false, hscroll = false;
53605
53606         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
53607
53608         var lw = this.lockedWrap, mw = this.mainWrap;
53609         var lb = this.lockedBody, mb = this.mainBody;
53610
53611         setTimeout(function(){
53612             var t = s.dom.offsetTop;
53613             var w = s.dom.clientWidth,
53614                 h = s.dom.clientHeight;
53615
53616             lw.setTop(t);
53617             lw.setSize(ltWidth, h);
53618
53619             mw.setLeftTop(ltWidth, t);
53620             mw.setSize(w-ltWidth, h);
53621
53622             lb.setHeight(h-hdHeight);
53623             mb.setHeight(h-hdHeight);
53624
53625             if(is2ndPass !== true && !gv.userResized && expandCol){
53626                 // high speed resize without full column calculation
53627                 
53628                 var ci = cm.getIndexById(expandCol);
53629                 if (ci < 0) {
53630                     ci = cm.findColumnIndex(expandCol);
53631                 }
53632                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53633                 var expandId = cm.getColumnId(ci);
53634                 var  tw = cm.getTotalWidth(false);
53635                 var currentWidth = cm.getColumnWidth(ci);
53636                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53637                 if(currentWidth != cw){
53638                     cm.setColumnWidth(ci, cw, true);
53639                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53640                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53641                     gv.updateSplitters();
53642                     gv.layout(false, true);
53643                 }
53644             }
53645
53646             if(initialRender){
53647                 lw.show();
53648                 mw.show();
53649             }
53650             //c.endMeasure();
53651         }, 10);
53652     },
53653
53654     onWindowResize : function(){
53655         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53656             return;
53657         }
53658         this.layout();
53659     },
53660
53661     appendFooter : function(parentEl){
53662         return null;
53663     },
53664
53665     sortAscText : "Sort Ascending",
53666     sortDescText : "Sort Descending",
53667     lockText : "Lock Column",
53668     unlockText : "Unlock Column",
53669     columnsText : "Columns"
53670 });
53671
53672
53673 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53674     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53675     this.proxy.el.addClass('x-grid3-col-dd');
53676 };
53677
53678 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53679     handleMouseDown : function(e){
53680
53681     },
53682
53683     callHandleMouseDown : function(e){
53684         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53685     }
53686 });
53687 /*
53688  * Based on:
53689  * Ext JS Library 1.1.1
53690  * Copyright(c) 2006-2007, Ext JS, LLC.
53691  *
53692  * Originally Released Under LGPL - original licence link has changed is not relivant.
53693  *
53694  * Fork - LGPL
53695  * <script type="text/javascript">
53696  */
53697  
53698 // private
53699 // This is a support class used internally by the Grid components
53700 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53701     this.grid = grid;
53702     this.view = grid.getView();
53703     this.proxy = this.view.resizeProxy;
53704     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53705         "gridSplitters" + this.grid.getGridEl().id, {
53706         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53707     });
53708     this.setHandleElId(Roo.id(hd));
53709     this.setOuterHandleElId(Roo.id(hd2));
53710     this.scroll = false;
53711 };
53712 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53713     fly: Roo.Element.fly,
53714
53715     b4StartDrag : function(x, y){
53716         this.view.headersDisabled = true;
53717         this.proxy.setHeight(this.view.mainWrap.getHeight());
53718         var w = this.cm.getColumnWidth(this.cellIndex);
53719         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53720         this.resetConstraints();
53721         this.setXConstraint(minw, 1000);
53722         this.setYConstraint(0, 0);
53723         this.minX = x - minw;
53724         this.maxX = x + 1000;
53725         this.startPos = x;
53726         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53727     },
53728
53729
53730     handleMouseDown : function(e){
53731         ev = Roo.EventObject.setEvent(e);
53732         var t = this.fly(ev.getTarget());
53733         if(t.hasClass("x-grid-split")){
53734             this.cellIndex = this.view.getCellIndex(t.dom);
53735             this.split = t.dom;
53736             this.cm = this.grid.colModel;
53737             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53738                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53739             }
53740         }
53741     },
53742
53743     endDrag : function(e){
53744         this.view.headersDisabled = false;
53745         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53746         var diff = endX - this.startPos;
53747         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53748     },
53749
53750     autoOffset : function(){
53751         this.setDelta(0,0);
53752     }
53753 });/*
53754  * Based on:
53755  * Ext JS Library 1.1.1
53756  * Copyright(c) 2006-2007, Ext JS, LLC.
53757  *
53758  * Originally Released Under LGPL - original licence link has changed is not relivant.
53759  *
53760  * Fork - LGPL
53761  * <script type="text/javascript">
53762  */
53763  
53764 // private
53765 // This is a support class used internally by the Grid components
53766 Roo.grid.GridDragZone = function(grid, config){
53767     this.view = grid.getView();
53768     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53769     if(this.view.lockedBody){
53770         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53771         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53772     }
53773     this.scroll = false;
53774     this.grid = grid;
53775     this.ddel = document.createElement('div');
53776     this.ddel.className = 'x-grid-dd-wrap';
53777 };
53778
53779 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53780     ddGroup : "GridDD",
53781
53782     getDragData : function(e){
53783         var t = Roo.lib.Event.getTarget(e);
53784         var rowIndex = this.view.findRowIndex(t);
53785         var sm = this.grid.selModel;
53786             
53787         //Roo.log(rowIndex);
53788         
53789         if (sm.getSelectedCell) {
53790             // cell selection..
53791             if (!sm.getSelectedCell()) {
53792                 return false;
53793             }
53794             if (rowIndex != sm.getSelectedCell()[0]) {
53795                 return false;
53796             }
53797         
53798         }
53799         
53800         if(rowIndex !== false){
53801             
53802             // if editorgrid.. 
53803             
53804             
53805             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53806                
53807             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53808               //  
53809             //}
53810             if (e.hasModifier()){
53811                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53812             }
53813             
53814             Roo.log("getDragData");
53815             
53816             return {
53817                 grid: this.grid,
53818                 ddel: this.ddel,
53819                 rowIndex: rowIndex,
53820                 selections:sm.getSelections ? sm.getSelections() : (
53821                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53822                 )
53823             };
53824         }
53825         return false;
53826     },
53827
53828     onInitDrag : function(e){
53829         var data = this.dragData;
53830         this.ddel.innerHTML = this.grid.getDragDropText();
53831         this.proxy.update(this.ddel);
53832         // fire start drag?
53833     },
53834
53835     afterRepair : function(){
53836         this.dragging = false;
53837     },
53838
53839     getRepairXY : function(e, data){
53840         return false;
53841     },
53842
53843     onEndDrag : function(data, e){
53844         // fire end drag?
53845     },
53846
53847     onValidDrop : function(dd, e, id){
53848         // fire drag drop?
53849         this.hideProxy();
53850     },
53851
53852     beforeInvalidDrop : function(e, id){
53853
53854     }
53855 });/*
53856  * Based on:
53857  * Ext JS Library 1.1.1
53858  * Copyright(c) 2006-2007, Ext JS, LLC.
53859  *
53860  * Originally Released Under LGPL - original licence link has changed is not relivant.
53861  *
53862  * Fork - LGPL
53863  * <script type="text/javascript">
53864  */
53865  
53866
53867 /**
53868  * @class Roo.grid.ColumnModel
53869  * @extends Roo.util.Observable
53870  * This is the default implementation of a ColumnModel used by the Grid. It defines
53871  * the columns in the grid.
53872  * <br>Usage:<br>
53873  <pre><code>
53874  var colModel = new Roo.grid.ColumnModel([
53875         {header: "Ticker", width: 60, sortable: true, locked: true},
53876         {header: "Company Name", width: 150, sortable: true},
53877         {header: "Market Cap.", width: 100, sortable: true},
53878         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53879         {header: "Employees", width: 100, sortable: true, resizable: false}
53880  ]);
53881  </code></pre>
53882  * <p>
53883  
53884  * The config options listed for this class are options which may appear in each
53885  * individual column definition.
53886  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53887  * @constructor
53888  * @param {Object} config An Array of column config objects. See this class's
53889  * config objects for details.
53890 */
53891 Roo.grid.ColumnModel = function(config){
53892         /**
53893      * The config passed into the constructor
53894      */
53895     this.config = config;
53896     this.lookup = {};
53897
53898     // if no id, create one
53899     // if the column does not have a dataIndex mapping,
53900     // map it to the order it is in the config
53901     for(var i = 0, len = config.length; i < len; i++){
53902         var c = config[i];
53903         if(typeof c.dataIndex == "undefined"){
53904             c.dataIndex = i;
53905         }
53906         if(typeof c.renderer == "string"){
53907             c.renderer = Roo.util.Format[c.renderer];
53908         }
53909         if(typeof c.id == "undefined"){
53910             c.id = Roo.id();
53911         }
53912         if(c.editor && c.editor.xtype){
53913             c.editor  = Roo.factory(c.editor, Roo.grid);
53914         }
53915         if(c.editor && c.editor.isFormField){
53916             c.editor = new Roo.grid.GridEditor(c.editor);
53917         }
53918         this.lookup[c.id] = c;
53919     }
53920
53921     /**
53922      * The width of columns which have no width specified (defaults to 100)
53923      * @type Number
53924      */
53925     this.defaultWidth = 100;
53926
53927     /**
53928      * Default sortable of columns which have no sortable specified (defaults to false)
53929      * @type Boolean
53930      */
53931     this.defaultSortable = false;
53932
53933     this.addEvents({
53934         /**
53935              * @event widthchange
53936              * Fires when the width of a column changes.
53937              * @param {ColumnModel} this
53938              * @param {Number} columnIndex The column index
53939              * @param {Number} newWidth The new width
53940              */
53941             "widthchange": true,
53942         /**
53943              * @event headerchange
53944              * Fires when the text of a header changes.
53945              * @param {ColumnModel} this
53946              * @param {Number} columnIndex The column index
53947              * @param {Number} newText The new header text
53948              */
53949             "headerchange": true,
53950         /**
53951              * @event hiddenchange
53952              * Fires when a column is hidden or "unhidden".
53953              * @param {ColumnModel} this
53954              * @param {Number} columnIndex The column index
53955              * @param {Boolean} hidden true if hidden, false otherwise
53956              */
53957             "hiddenchange": true,
53958             /**
53959          * @event columnmoved
53960          * Fires when a column is moved.
53961          * @param {ColumnModel} this
53962          * @param {Number} oldIndex
53963          * @param {Number} newIndex
53964          */
53965         "columnmoved" : true,
53966         /**
53967          * @event columlockchange
53968          * Fires when a column's locked state is changed
53969          * @param {ColumnModel} this
53970          * @param {Number} colIndex
53971          * @param {Boolean} locked true if locked
53972          */
53973         "columnlockchange" : true
53974     });
53975     Roo.grid.ColumnModel.superclass.constructor.call(this);
53976 };
53977 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
53978     /**
53979      * @cfg {String} header The header text to display in the Grid view.
53980      */
53981     /**
53982      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
53983      * {@link Roo.data.Record} definition from which to draw the column's value. If not
53984      * specified, the column's index is used as an index into the Record's data Array.
53985      */
53986     /**
53987      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
53988      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
53989      */
53990     /**
53991      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
53992      * Defaults to the value of the {@link #defaultSortable} property.
53993      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
53994      */
53995     /**
53996      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
53997      */
53998     /**
53999      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54000      */
54001     /**
54002      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54003      */
54004     /**
54005      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54006      */
54007     /**
54008      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54009      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54010      * default renderer uses the raw data value.
54011      */
54012        /**
54013      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54014      */
54015     /**
54016      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54017      */
54018
54019     /**
54020      * Returns the id of the column at the specified index.
54021      * @param {Number} index The column index
54022      * @return {String} the id
54023      */
54024     getColumnId : function(index){
54025         return this.config[index].id;
54026     },
54027
54028     /**
54029      * Returns the column for a specified id.
54030      * @param {String} id The column id
54031      * @return {Object} the column
54032      */
54033     getColumnById : function(id){
54034         return this.lookup[id];
54035     },
54036
54037     
54038     /**
54039      * Returns the column for a specified dataIndex.
54040      * @param {String} dataIndex The column dataIndex
54041      * @return {Object|Boolean} the column or false if not found
54042      */
54043     getColumnByDataIndex: function(dataIndex){
54044         var index = this.findColumnIndex(dataIndex);
54045         return index > -1 ? this.config[index] : false;
54046     },
54047     
54048     /**
54049      * Returns the index for a specified column id.
54050      * @param {String} id The column id
54051      * @return {Number} the index, or -1 if not found
54052      */
54053     getIndexById : function(id){
54054         for(var i = 0, len = this.config.length; i < len; i++){
54055             if(this.config[i].id == id){
54056                 return i;
54057             }
54058         }
54059         return -1;
54060     },
54061     
54062     /**
54063      * Returns the index for a specified column dataIndex.
54064      * @param {String} dataIndex The column dataIndex
54065      * @return {Number} the index, or -1 if not found
54066      */
54067     
54068     findColumnIndex : function(dataIndex){
54069         for(var i = 0, len = this.config.length; i < len; i++){
54070             if(this.config[i].dataIndex == dataIndex){
54071                 return i;
54072             }
54073         }
54074         return -1;
54075     },
54076     
54077     
54078     moveColumn : function(oldIndex, newIndex){
54079         var c = this.config[oldIndex];
54080         this.config.splice(oldIndex, 1);
54081         this.config.splice(newIndex, 0, c);
54082         this.dataMap = null;
54083         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54084     },
54085
54086     isLocked : function(colIndex){
54087         return this.config[colIndex].locked === true;
54088     },
54089
54090     setLocked : function(colIndex, value, suppressEvent){
54091         if(this.isLocked(colIndex) == value){
54092             return;
54093         }
54094         this.config[colIndex].locked = value;
54095         if(!suppressEvent){
54096             this.fireEvent("columnlockchange", this, colIndex, value);
54097         }
54098     },
54099
54100     getTotalLockedWidth : function(){
54101         var totalWidth = 0;
54102         for(var i = 0; i < this.config.length; i++){
54103             if(this.isLocked(i) && !this.isHidden(i)){
54104                 this.totalWidth += this.getColumnWidth(i);
54105             }
54106         }
54107         return totalWidth;
54108     },
54109
54110     getLockedCount : function(){
54111         for(var i = 0, len = this.config.length; i < len; i++){
54112             if(!this.isLocked(i)){
54113                 return i;
54114             }
54115         }
54116     },
54117
54118     /**
54119      * Returns the number of columns.
54120      * @return {Number}
54121      */
54122     getColumnCount : function(visibleOnly){
54123         if(visibleOnly === true){
54124             var c = 0;
54125             for(var i = 0, len = this.config.length; i < len; i++){
54126                 if(!this.isHidden(i)){
54127                     c++;
54128                 }
54129             }
54130             return c;
54131         }
54132         return this.config.length;
54133     },
54134
54135     /**
54136      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54137      * @param {Function} fn
54138      * @param {Object} scope (optional)
54139      * @return {Array} result
54140      */
54141     getColumnsBy : function(fn, scope){
54142         var r = [];
54143         for(var i = 0, len = this.config.length; i < len; i++){
54144             var c = this.config[i];
54145             if(fn.call(scope||this, c, i) === true){
54146                 r[r.length] = c;
54147             }
54148         }
54149         return r;
54150     },
54151
54152     /**
54153      * Returns true if the specified column is sortable.
54154      * @param {Number} col The column index
54155      * @return {Boolean}
54156      */
54157     isSortable : function(col){
54158         if(typeof this.config[col].sortable == "undefined"){
54159             return this.defaultSortable;
54160         }
54161         return this.config[col].sortable;
54162     },
54163
54164     /**
54165      * Returns the rendering (formatting) function defined for the column.
54166      * @param {Number} col The column index.
54167      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54168      */
54169     getRenderer : function(col){
54170         if(!this.config[col].renderer){
54171             return Roo.grid.ColumnModel.defaultRenderer;
54172         }
54173         return this.config[col].renderer;
54174     },
54175
54176     /**
54177      * Sets the rendering (formatting) function for a column.
54178      * @param {Number} col The column index
54179      * @param {Function} fn The function to use to process the cell's raw data
54180      * to return HTML markup for the grid view. The render function is called with
54181      * the following parameters:<ul>
54182      * <li>Data value.</li>
54183      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54184      * <li>css A CSS style string to apply to the table cell.</li>
54185      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54186      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54187      * <li>Row index</li>
54188      * <li>Column index</li>
54189      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54190      */
54191     setRenderer : function(col, fn){
54192         this.config[col].renderer = fn;
54193     },
54194
54195     /**
54196      * Returns the width for the specified column.
54197      * @param {Number} col The column index
54198      * @return {Number}
54199      */
54200     getColumnWidth : function(col){
54201         return this.config[col].width * 1 || this.defaultWidth;
54202     },
54203
54204     /**
54205      * Sets the width for a column.
54206      * @param {Number} col The column index
54207      * @param {Number} width The new width
54208      */
54209     setColumnWidth : function(col, width, suppressEvent){
54210         this.config[col].width = width;
54211         this.totalWidth = null;
54212         if(!suppressEvent){
54213              this.fireEvent("widthchange", this, col, width);
54214         }
54215     },
54216
54217     /**
54218      * Returns the total width of all columns.
54219      * @param {Boolean} includeHidden True to include hidden column widths
54220      * @return {Number}
54221      */
54222     getTotalWidth : function(includeHidden){
54223         if(!this.totalWidth){
54224             this.totalWidth = 0;
54225             for(var i = 0, len = this.config.length; i < len; i++){
54226                 if(includeHidden || !this.isHidden(i)){
54227                     this.totalWidth += this.getColumnWidth(i);
54228                 }
54229             }
54230         }
54231         return this.totalWidth;
54232     },
54233
54234     /**
54235      * Returns the header for the specified column.
54236      * @param {Number} col The column index
54237      * @return {String}
54238      */
54239     getColumnHeader : function(col){
54240         return this.config[col].header;
54241     },
54242
54243     /**
54244      * Sets the header for a column.
54245      * @param {Number} col The column index
54246      * @param {String} header The new header
54247      */
54248     setColumnHeader : function(col, header){
54249         this.config[col].header = header;
54250         this.fireEvent("headerchange", this, col, header);
54251     },
54252
54253     /**
54254      * Returns the tooltip for the specified column.
54255      * @param {Number} col The column index
54256      * @return {String}
54257      */
54258     getColumnTooltip : function(col){
54259             return this.config[col].tooltip;
54260     },
54261     /**
54262      * Sets the tooltip for a column.
54263      * @param {Number} col The column index
54264      * @param {String} tooltip The new tooltip
54265      */
54266     setColumnTooltip : function(col, tooltip){
54267             this.config[col].tooltip = tooltip;
54268     },
54269
54270     /**
54271      * Returns the dataIndex for the specified column.
54272      * @param {Number} col The column index
54273      * @return {Number}
54274      */
54275     getDataIndex : function(col){
54276         return this.config[col].dataIndex;
54277     },
54278
54279     /**
54280      * Sets the dataIndex for a column.
54281      * @param {Number} col The column index
54282      * @param {Number} dataIndex The new dataIndex
54283      */
54284     setDataIndex : function(col, dataIndex){
54285         this.config[col].dataIndex = dataIndex;
54286     },
54287
54288     
54289     
54290     /**
54291      * Returns true if the cell is editable.
54292      * @param {Number} colIndex The column index
54293      * @param {Number} rowIndex The row index
54294      * @return {Boolean}
54295      */
54296     isCellEditable : function(colIndex, rowIndex){
54297         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54298     },
54299
54300     /**
54301      * Returns the editor defined for the cell/column.
54302      * return false or null to disable editing.
54303      * @param {Number} colIndex The column index
54304      * @param {Number} rowIndex The row index
54305      * @return {Object}
54306      */
54307     getCellEditor : function(colIndex, rowIndex){
54308         return this.config[colIndex].editor;
54309     },
54310
54311     /**
54312      * Sets if a column is editable.
54313      * @param {Number} col The column index
54314      * @param {Boolean} editable True if the column is editable
54315      */
54316     setEditable : function(col, editable){
54317         this.config[col].editable = editable;
54318     },
54319
54320
54321     /**
54322      * Returns true if the column is hidden.
54323      * @param {Number} colIndex The column index
54324      * @return {Boolean}
54325      */
54326     isHidden : function(colIndex){
54327         return this.config[colIndex].hidden;
54328     },
54329
54330
54331     /**
54332      * Returns true if the column width cannot be changed
54333      */
54334     isFixed : function(colIndex){
54335         return this.config[colIndex].fixed;
54336     },
54337
54338     /**
54339      * Returns true if the column can be resized
54340      * @return {Boolean}
54341      */
54342     isResizable : function(colIndex){
54343         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54344     },
54345     /**
54346      * Sets if a column is hidden.
54347      * @param {Number} colIndex The column index
54348      * @param {Boolean} hidden True if the column is hidden
54349      */
54350     setHidden : function(colIndex, hidden){
54351         this.config[colIndex].hidden = hidden;
54352         this.totalWidth = null;
54353         this.fireEvent("hiddenchange", this, colIndex, hidden);
54354     },
54355
54356     /**
54357      * Sets the editor for a column.
54358      * @param {Number} col The column index
54359      * @param {Object} editor The editor object
54360      */
54361     setEditor : function(col, editor){
54362         this.config[col].editor = editor;
54363     }
54364 });
54365
54366 Roo.grid.ColumnModel.defaultRenderer = function(value){
54367         if(typeof value == "string" && value.length < 1){
54368             return "&#160;";
54369         }
54370         return value;
54371 };
54372
54373 // Alias for backwards compatibility
54374 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54375 /*
54376  * Based on:
54377  * Ext JS Library 1.1.1
54378  * Copyright(c) 2006-2007, Ext JS, LLC.
54379  *
54380  * Originally Released Under LGPL - original licence link has changed is not relivant.
54381  *
54382  * Fork - LGPL
54383  * <script type="text/javascript">
54384  */
54385
54386 /**
54387  * @class Roo.grid.AbstractSelectionModel
54388  * @extends Roo.util.Observable
54389  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54390  * implemented by descendant classes.  This class should not be directly instantiated.
54391  * @constructor
54392  */
54393 Roo.grid.AbstractSelectionModel = function(){
54394     this.locked = false;
54395     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54396 };
54397
54398 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54399     /** @ignore Called by the grid automatically. Do not call directly. */
54400     init : function(grid){
54401         this.grid = grid;
54402         this.initEvents();
54403     },
54404
54405     /**
54406      * Locks the selections.
54407      */
54408     lock : function(){
54409         this.locked = true;
54410     },
54411
54412     /**
54413      * Unlocks the selections.
54414      */
54415     unlock : function(){
54416         this.locked = false;
54417     },
54418
54419     /**
54420      * Returns true if the selections are locked.
54421      * @return {Boolean}
54422      */
54423     isLocked : function(){
54424         return this.locked;
54425     }
54426 });/*
54427  * Based on:
54428  * Ext JS Library 1.1.1
54429  * Copyright(c) 2006-2007, Ext JS, LLC.
54430  *
54431  * Originally Released Under LGPL - original licence link has changed is not relivant.
54432  *
54433  * Fork - LGPL
54434  * <script type="text/javascript">
54435  */
54436 /**
54437  * @extends Roo.grid.AbstractSelectionModel
54438  * @class Roo.grid.RowSelectionModel
54439  * The default SelectionModel used by {@link Roo.grid.Grid}.
54440  * It supports multiple selections and keyboard selection/navigation. 
54441  * @constructor
54442  * @param {Object} config
54443  */
54444 Roo.grid.RowSelectionModel = function(config){
54445     Roo.apply(this, config);
54446     this.selections = new Roo.util.MixedCollection(false, function(o){
54447         return o.id;
54448     });
54449
54450     this.last = false;
54451     this.lastActive = false;
54452
54453     this.addEvents({
54454         /**
54455              * @event selectionchange
54456              * Fires when the selection changes
54457              * @param {SelectionModel} this
54458              */
54459             "selectionchange" : true,
54460         /**
54461              * @event afterselectionchange
54462              * Fires after the selection changes (eg. by key press or clicking)
54463              * @param {SelectionModel} this
54464              */
54465             "afterselectionchange" : true,
54466         /**
54467              * @event beforerowselect
54468              * Fires when a row is selected being selected, return false to cancel.
54469              * @param {SelectionModel} this
54470              * @param {Number} rowIndex The selected index
54471              * @param {Boolean} keepExisting False if other selections will be cleared
54472              */
54473             "beforerowselect" : true,
54474         /**
54475              * @event rowselect
54476              * Fires when a row is selected.
54477              * @param {SelectionModel} this
54478              * @param {Number} rowIndex The selected index
54479              * @param {Roo.data.Record} r The record
54480              */
54481             "rowselect" : true,
54482         /**
54483              * @event rowdeselect
54484              * Fires when a row is deselected.
54485              * @param {SelectionModel} this
54486              * @param {Number} rowIndex The selected index
54487              */
54488         "rowdeselect" : true
54489     });
54490     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54491     this.locked = false;
54492 };
54493
54494 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54495     /**
54496      * @cfg {Boolean} singleSelect
54497      * True to allow selection of only one row at a time (defaults to false)
54498      */
54499     singleSelect : false,
54500
54501     // private
54502     initEvents : function(){
54503
54504         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54505             this.grid.on("mousedown", this.handleMouseDown, this);
54506         }else{ // allow click to work like normal
54507             this.grid.on("rowclick", this.handleDragableRowClick, this);
54508         }
54509
54510         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54511             "up" : function(e){
54512                 if(!e.shiftKey){
54513                     this.selectPrevious(e.shiftKey);
54514                 }else if(this.last !== false && this.lastActive !== false){
54515                     var last = this.last;
54516                     this.selectRange(this.last,  this.lastActive-1);
54517                     this.grid.getView().focusRow(this.lastActive);
54518                     if(last !== false){
54519                         this.last = last;
54520                     }
54521                 }else{
54522                     this.selectFirstRow();
54523                 }
54524                 this.fireEvent("afterselectionchange", this);
54525             },
54526             "down" : function(e){
54527                 if(!e.shiftKey){
54528                     this.selectNext(e.shiftKey);
54529                 }else if(this.last !== false && this.lastActive !== false){
54530                     var last = this.last;
54531                     this.selectRange(this.last,  this.lastActive+1);
54532                     this.grid.getView().focusRow(this.lastActive);
54533                     if(last !== false){
54534                         this.last = last;
54535                     }
54536                 }else{
54537                     this.selectFirstRow();
54538                 }
54539                 this.fireEvent("afterselectionchange", this);
54540             },
54541             scope: this
54542         });
54543
54544         var view = this.grid.view;
54545         view.on("refresh", this.onRefresh, this);
54546         view.on("rowupdated", this.onRowUpdated, this);
54547         view.on("rowremoved", this.onRemove, this);
54548     },
54549
54550     // private
54551     onRefresh : function(){
54552         var ds = this.grid.dataSource, i, v = this.grid.view;
54553         var s = this.selections;
54554         s.each(function(r){
54555             if((i = ds.indexOfId(r.id)) != -1){
54556                 v.onRowSelect(i);
54557             }else{
54558                 s.remove(r);
54559             }
54560         });
54561     },
54562
54563     // private
54564     onRemove : function(v, index, r){
54565         this.selections.remove(r);
54566     },
54567
54568     // private
54569     onRowUpdated : function(v, index, r){
54570         if(this.isSelected(r)){
54571             v.onRowSelect(index);
54572         }
54573     },
54574
54575     /**
54576      * Select records.
54577      * @param {Array} records The records to select
54578      * @param {Boolean} keepExisting (optional) True to keep existing selections
54579      */
54580     selectRecords : function(records, keepExisting){
54581         if(!keepExisting){
54582             this.clearSelections();
54583         }
54584         var ds = this.grid.dataSource;
54585         for(var i = 0, len = records.length; i < len; i++){
54586             this.selectRow(ds.indexOf(records[i]), true);
54587         }
54588     },
54589
54590     /**
54591      * Gets the number of selected rows.
54592      * @return {Number}
54593      */
54594     getCount : function(){
54595         return this.selections.length;
54596     },
54597
54598     /**
54599      * Selects the first row in the grid.
54600      */
54601     selectFirstRow : function(){
54602         this.selectRow(0);
54603     },
54604
54605     /**
54606      * Select the last row.
54607      * @param {Boolean} keepExisting (optional) True to keep existing selections
54608      */
54609     selectLastRow : function(keepExisting){
54610         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
54611     },
54612
54613     /**
54614      * Selects the row immediately following the last selected row.
54615      * @param {Boolean} keepExisting (optional) True to keep existing selections
54616      */
54617     selectNext : function(keepExisting){
54618         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54619             this.selectRow(this.last+1, keepExisting);
54620             this.grid.getView().focusRow(this.last);
54621         }
54622     },
54623
54624     /**
54625      * Selects the row that precedes the last selected row.
54626      * @param {Boolean} keepExisting (optional) True to keep existing selections
54627      */
54628     selectPrevious : function(keepExisting){
54629         if(this.last){
54630             this.selectRow(this.last-1, keepExisting);
54631             this.grid.getView().focusRow(this.last);
54632         }
54633     },
54634
54635     /**
54636      * Returns the selected records
54637      * @return {Array} Array of selected records
54638      */
54639     getSelections : function(){
54640         return [].concat(this.selections.items);
54641     },
54642
54643     /**
54644      * Returns the first selected record.
54645      * @return {Record}
54646      */
54647     getSelected : function(){
54648         return this.selections.itemAt(0);
54649     },
54650
54651
54652     /**
54653      * Clears all selections.
54654      */
54655     clearSelections : function(fast){
54656         if(this.locked) return;
54657         if(fast !== true){
54658             var ds = this.grid.dataSource;
54659             var s = this.selections;
54660             s.each(function(r){
54661                 this.deselectRow(ds.indexOfId(r.id));
54662             }, this);
54663             s.clear();
54664         }else{
54665             this.selections.clear();
54666         }
54667         this.last = false;
54668     },
54669
54670
54671     /**
54672      * Selects all rows.
54673      */
54674     selectAll : function(){
54675         if(this.locked) return;
54676         this.selections.clear();
54677         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54678             this.selectRow(i, true);
54679         }
54680     },
54681
54682     /**
54683      * Returns True if there is a selection.
54684      * @return {Boolean}
54685      */
54686     hasSelection : function(){
54687         return this.selections.length > 0;
54688     },
54689
54690     /**
54691      * Returns True if the specified row is selected.
54692      * @param {Number/Record} record The record or index of the record to check
54693      * @return {Boolean}
54694      */
54695     isSelected : function(index){
54696         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54697         return (r && this.selections.key(r.id) ? true : false);
54698     },
54699
54700     /**
54701      * Returns True if the specified record id is selected.
54702      * @param {String} id The id of record to check
54703      * @return {Boolean}
54704      */
54705     isIdSelected : function(id){
54706         return (this.selections.key(id) ? true : false);
54707     },
54708
54709     // private
54710     handleMouseDown : function(e, t){
54711         var view = this.grid.getView(), rowIndex;
54712         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54713             return;
54714         };
54715         if(e.shiftKey && this.last !== false){
54716             var last = this.last;
54717             this.selectRange(last, rowIndex, e.ctrlKey);
54718             this.last = last; // reset the last
54719             view.focusRow(rowIndex);
54720         }else{
54721             var isSelected = this.isSelected(rowIndex);
54722             if(e.button !== 0 && isSelected){
54723                 view.focusRow(rowIndex);
54724             }else if(e.ctrlKey && isSelected){
54725                 this.deselectRow(rowIndex);
54726             }else if(!isSelected){
54727                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54728                 view.focusRow(rowIndex);
54729             }
54730         }
54731         this.fireEvent("afterselectionchange", this);
54732     },
54733     // private
54734     handleDragableRowClick :  function(grid, rowIndex, e) 
54735     {
54736         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54737             this.selectRow(rowIndex, false);
54738             grid.view.focusRow(rowIndex);
54739              this.fireEvent("afterselectionchange", this);
54740         }
54741     },
54742     
54743     /**
54744      * Selects multiple rows.
54745      * @param {Array} rows Array of the indexes of the row to select
54746      * @param {Boolean} keepExisting (optional) True to keep existing selections
54747      */
54748     selectRows : function(rows, keepExisting){
54749         if(!keepExisting){
54750             this.clearSelections();
54751         }
54752         for(var i = 0, len = rows.length; i < len; i++){
54753             this.selectRow(rows[i], true);
54754         }
54755     },
54756
54757     /**
54758      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54759      * @param {Number} startRow The index of the first row in the range
54760      * @param {Number} endRow The index of the last row in the range
54761      * @param {Boolean} keepExisting (optional) True to retain existing selections
54762      */
54763     selectRange : function(startRow, endRow, keepExisting){
54764         if(this.locked) return;
54765         if(!keepExisting){
54766             this.clearSelections();
54767         }
54768         if(startRow <= endRow){
54769             for(var i = startRow; i <= endRow; i++){
54770                 this.selectRow(i, true);
54771             }
54772         }else{
54773             for(var i = startRow; i >= endRow; i--){
54774                 this.selectRow(i, true);
54775             }
54776         }
54777     },
54778
54779     /**
54780      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54781      * @param {Number} startRow The index of the first row in the range
54782      * @param {Number} endRow The index of the last row in the range
54783      */
54784     deselectRange : function(startRow, endRow, preventViewNotify){
54785         if(this.locked) return;
54786         for(var i = startRow; i <= endRow; i++){
54787             this.deselectRow(i, preventViewNotify);
54788         }
54789     },
54790
54791     /**
54792      * Selects a row.
54793      * @param {Number} row The index of the row to select
54794      * @param {Boolean} keepExisting (optional) True to keep existing selections
54795      */
54796     selectRow : function(index, keepExisting, preventViewNotify){
54797         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54798         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54799             if(!keepExisting || this.singleSelect){
54800                 this.clearSelections();
54801             }
54802             var r = this.grid.dataSource.getAt(index);
54803             this.selections.add(r);
54804             this.last = this.lastActive = index;
54805             if(!preventViewNotify){
54806                 this.grid.getView().onRowSelect(index);
54807             }
54808             this.fireEvent("rowselect", this, index, r);
54809             this.fireEvent("selectionchange", this);
54810         }
54811     },
54812
54813     /**
54814      * Deselects a row.
54815      * @param {Number} row The index of the row to deselect
54816      */
54817     deselectRow : function(index, preventViewNotify){
54818         if(this.locked) return;
54819         if(this.last == index){
54820             this.last = false;
54821         }
54822         if(this.lastActive == index){
54823             this.lastActive = false;
54824         }
54825         var r = this.grid.dataSource.getAt(index);
54826         this.selections.remove(r);
54827         if(!preventViewNotify){
54828             this.grid.getView().onRowDeselect(index);
54829         }
54830         this.fireEvent("rowdeselect", this, index);
54831         this.fireEvent("selectionchange", this);
54832     },
54833
54834     // private
54835     restoreLast : function(){
54836         if(this._last){
54837             this.last = this._last;
54838         }
54839     },
54840
54841     // private
54842     acceptsNav : function(row, col, cm){
54843         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54844     },
54845
54846     // private
54847     onEditorKey : function(field, e){
54848         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54849         if(k == e.TAB){
54850             e.stopEvent();
54851             ed.completeEdit();
54852             if(e.shiftKey){
54853                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54854             }else{
54855                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54856             }
54857         }else if(k == e.ENTER && !e.ctrlKey){
54858             e.stopEvent();
54859             ed.completeEdit();
54860             if(e.shiftKey){
54861                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54862             }else{
54863                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54864             }
54865         }else if(k == e.ESC){
54866             ed.cancelEdit();
54867         }
54868         if(newCell){
54869             g.startEditing(newCell[0], newCell[1]);
54870         }
54871     }
54872 });/*
54873  * Based on:
54874  * Ext JS Library 1.1.1
54875  * Copyright(c) 2006-2007, Ext JS, LLC.
54876  *
54877  * Originally Released Under LGPL - original licence link has changed is not relivant.
54878  *
54879  * Fork - LGPL
54880  * <script type="text/javascript">
54881  */
54882 /**
54883  * @class Roo.grid.CellSelectionModel
54884  * @extends Roo.grid.AbstractSelectionModel
54885  * This class provides the basic implementation for cell selection in a grid.
54886  * @constructor
54887  * @param {Object} config The object containing the configuration of this model.
54888  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54889  */
54890 Roo.grid.CellSelectionModel = function(config){
54891     Roo.apply(this, config);
54892
54893     this.selection = null;
54894
54895     this.addEvents({
54896         /**
54897              * @event beforerowselect
54898              * Fires before a cell is selected.
54899              * @param {SelectionModel} this
54900              * @param {Number} rowIndex The selected row index
54901              * @param {Number} colIndex The selected cell index
54902              */
54903             "beforecellselect" : true,
54904         /**
54905              * @event cellselect
54906              * Fires when a cell is selected.
54907              * @param {SelectionModel} this
54908              * @param {Number} rowIndex The selected row index
54909              * @param {Number} colIndex The selected cell index
54910              */
54911             "cellselect" : true,
54912         /**
54913              * @event selectionchange
54914              * Fires when the active selection changes.
54915              * @param {SelectionModel} this
54916              * @param {Object} selection null for no selection or an object (o) with two properties
54917                 <ul>
54918                 <li>o.record: the record object for the row the selection is in</li>
54919                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54920                 </ul>
54921              */
54922             "selectionchange" : true,
54923         /**
54924              * @event tabend
54925              * Fires when the tab (or enter) was pressed on the last editable cell
54926              * You can use this to trigger add new row.
54927              * @param {SelectionModel} this
54928              */
54929             "tabend" : true,
54930          /**
54931              * @event beforeeditnext
54932              * Fires before the next editable sell is made active
54933              * You can use this to skip to another cell or fire the tabend
54934              *    if you set cell to false
54935              * @param {Object} eventdata object : { cell : [ row, col ] } 
54936              */
54937             "beforeeditnext" : true
54938     });
54939     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54940 };
54941
54942 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54943     
54944     enter_is_tab: false,
54945
54946     /** @ignore */
54947     initEvents : function(){
54948         this.grid.on("mousedown", this.handleMouseDown, this);
54949         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54950         var view = this.grid.view;
54951         view.on("refresh", this.onViewChange, this);
54952         view.on("rowupdated", this.onRowUpdated, this);
54953         view.on("beforerowremoved", this.clearSelections, this);
54954         view.on("beforerowsinserted", this.clearSelections, this);
54955         if(this.grid.isEditor){
54956             this.grid.on("beforeedit", this.beforeEdit,  this);
54957         }
54958     },
54959
54960         //private
54961     beforeEdit : function(e){
54962         this.select(e.row, e.column, false, true, e.record);
54963     },
54964
54965         //private
54966     onRowUpdated : function(v, index, r){
54967         if(this.selection && this.selection.record == r){
54968             v.onCellSelect(index, this.selection.cell[1]);
54969         }
54970     },
54971
54972         //private
54973     onViewChange : function(){
54974         this.clearSelections(true);
54975     },
54976
54977         /**
54978          * Returns the currently selected cell,.
54979          * @return {Array} The selected cell (row, column) or null if none selected.
54980          */
54981     getSelectedCell : function(){
54982         return this.selection ? this.selection.cell : null;
54983     },
54984
54985     /**
54986      * Clears all selections.
54987      * @param {Boolean} true to prevent the gridview from being notified about the change.
54988      */
54989     clearSelections : function(preventNotify){
54990         var s = this.selection;
54991         if(s){
54992             if(preventNotify !== true){
54993                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
54994             }
54995             this.selection = null;
54996             this.fireEvent("selectionchange", this, null);
54997         }
54998     },
54999
55000     /**
55001      * Returns true if there is a selection.
55002      * @return {Boolean}
55003      */
55004     hasSelection : function(){
55005         return this.selection ? true : false;
55006     },
55007
55008     /** @ignore */
55009     handleMouseDown : function(e, t){
55010         var v = this.grid.getView();
55011         if(this.isLocked()){
55012             return;
55013         };
55014         var row = v.findRowIndex(t);
55015         var cell = v.findCellIndex(t);
55016         if(row !== false && cell !== false){
55017             this.select(row, cell);
55018         }
55019     },
55020
55021     /**
55022      * Selects a cell.
55023      * @param {Number} rowIndex
55024      * @param {Number} collIndex
55025      */
55026     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55027         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55028             this.clearSelections();
55029             r = r || this.grid.dataSource.getAt(rowIndex);
55030             this.selection = {
55031                 record : r,
55032                 cell : [rowIndex, colIndex]
55033             };
55034             if(!preventViewNotify){
55035                 var v = this.grid.getView();
55036                 v.onCellSelect(rowIndex, colIndex);
55037                 if(preventFocus !== true){
55038                     v.focusCell(rowIndex, colIndex);
55039                 }
55040             }
55041             this.fireEvent("cellselect", this, rowIndex, colIndex);
55042             this.fireEvent("selectionchange", this, this.selection);
55043         }
55044     },
55045
55046         //private
55047     isSelectable : function(rowIndex, colIndex, cm){
55048         return !cm.isHidden(colIndex);
55049     },
55050
55051     /** @ignore */
55052     handleKeyDown : function(e){
55053         //Roo.log('Cell Sel Model handleKeyDown');
55054         if(!e.isNavKeyPress()){
55055             return;
55056         }
55057         var g = this.grid, s = this.selection;
55058         if(!s){
55059             e.stopEvent();
55060             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55061             if(cell){
55062                 this.select(cell[0], cell[1]);
55063             }
55064             return;
55065         }
55066         var sm = this;
55067         var walk = function(row, col, step){
55068             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55069         };
55070         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55071         var newCell;
55072
55073       
55074
55075         switch(k){
55076             case e.TAB:
55077                 // handled by onEditorKey
55078                 if (g.isEditor && g.editing) {
55079                     return;
55080                 }
55081                 if(e.shiftKey) {
55082                     newCell = walk(r, c-1, -1);
55083                 } else {
55084                     newCell = walk(r, c+1, 1);
55085                 }
55086                 break;
55087             
55088             case e.DOWN:
55089                newCell = walk(r+1, c, 1);
55090                 break;
55091             
55092             case e.UP:
55093                 newCell = walk(r-1, c, -1);
55094                 break;
55095             
55096             case e.RIGHT:
55097                 newCell = walk(r, c+1, 1);
55098                 break;
55099             
55100             case e.LEFT:
55101                 newCell = walk(r, c-1, -1);
55102                 break;
55103             
55104             case e.ENTER:
55105                 
55106                 if(g.isEditor && !g.editing){
55107                    g.startEditing(r, c);
55108                    e.stopEvent();
55109                    return;
55110                 }
55111                 
55112                 
55113              break;
55114         };
55115         if(newCell){
55116             this.select(newCell[0], newCell[1]);
55117             e.stopEvent();
55118             
55119         }
55120     },
55121
55122     acceptsNav : function(row, col, cm){
55123         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55124     },
55125     /**
55126      * Selects a cell.
55127      * @param {Number} field (not used) - as it's normally used as a listener
55128      * @param {Number} e - event - fake it by using
55129      *
55130      * var e = Roo.EventObjectImpl.prototype;
55131      * e.keyCode = e.TAB
55132      *
55133      * 
55134      */
55135     onEditorKey : function(field, e){
55136         
55137         var k = e.getKey(),
55138             newCell,
55139             g = this.grid,
55140             ed = g.activeEditor,
55141             forward = false;
55142         ///Roo.log('onEditorKey' + k);
55143         
55144         
55145         if (this.enter_is_tab && k == e.ENTER) {
55146             k = e.TAB;
55147         }
55148         
55149         if(k == e.TAB){
55150             if(e.shiftKey){
55151                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55152             }else{
55153                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55154                 forward = true;
55155             }
55156             
55157             e.stopEvent();
55158             
55159         } else if(k == e.ENTER &&  !e.ctrlKey){
55160             ed.completeEdit();
55161             e.stopEvent();
55162             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55163         
55164                 } else if(k == e.ESC){
55165             ed.cancelEdit();
55166         }
55167                 
55168         if (newCell) {
55169             var ecall = { cell : newCell, forward : forward };
55170             this.fireEvent('beforeeditnext', ecall );
55171             newCell = ecall.cell;
55172                         forward = ecall.forward;
55173         }
55174                 
55175         if(newCell){
55176             //Roo.log('next cell after edit');
55177             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55178         } else if (forward) {
55179             // tabbed past last
55180             this.fireEvent.defer(100, this, ['tabend',this]);
55181         }
55182     }
55183 });/*
55184  * Based on:
55185  * Ext JS Library 1.1.1
55186  * Copyright(c) 2006-2007, Ext JS, LLC.
55187  *
55188  * Originally Released Under LGPL - original licence link has changed is not relivant.
55189  *
55190  * Fork - LGPL
55191  * <script type="text/javascript">
55192  */
55193  
55194 /**
55195  * @class Roo.grid.EditorGrid
55196  * @extends Roo.grid.Grid
55197  * Class for creating and editable grid.
55198  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55199  * The container MUST have some type of size defined for the grid to fill. The container will be 
55200  * automatically set to position relative if it isn't already.
55201  * @param {Object} dataSource The data model to bind to
55202  * @param {Object} colModel The column model with info about this grid's columns
55203  */
55204 Roo.grid.EditorGrid = function(container, config){
55205     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55206     this.getGridEl().addClass("xedit-grid");
55207
55208     if(!this.selModel){
55209         this.selModel = new Roo.grid.CellSelectionModel();
55210     }
55211
55212     this.activeEditor = null;
55213
55214         this.addEvents({
55215             /**
55216              * @event beforeedit
55217              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55218              * <ul style="padding:5px;padding-left:16px;">
55219              * <li>grid - This grid</li>
55220              * <li>record - The record being edited</li>
55221              * <li>field - The field name being edited</li>
55222              * <li>value - The value for the field being edited.</li>
55223              * <li>row - The grid row index</li>
55224              * <li>column - The grid column index</li>
55225              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55226              * </ul>
55227              * @param {Object} e An edit event (see above for description)
55228              */
55229             "beforeedit" : true,
55230             /**
55231              * @event afteredit
55232              * Fires after a cell is edited. <br />
55233              * <ul style="padding:5px;padding-left:16px;">
55234              * <li>grid - This grid</li>
55235              * <li>record - The record being edited</li>
55236              * <li>field - The field name being edited</li>
55237              * <li>value - The value being set</li>
55238              * <li>originalValue - The original value for the field, before the edit.</li>
55239              * <li>row - The grid row index</li>
55240              * <li>column - The grid column index</li>
55241              * </ul>
55242              * @param {Object} e An edit event (see above for description)
55243              */
55244             "afteredit" : true,
55245             /**
55246              * @event validateedit
55247              * Fires after a cell is edited, but before the value is set in the record. 
55248          * You can use this to modify the value being set in the field, Return false
55249              * to cancel the change. The edit event object has the following properties <br />
55250              * <ul style="padding:5px;padding-left:16px;">
55251          * <li>editor - This editor</li>
55252              * <li>grid - This grid</li>
55253              * <li>record - The record being edited</li>
55254              * <li>field - The field name being edited</li>
55255              * <li>value - The value being set</li>
55256              * <li>originalValue - The original value for the field, before the edit.</li>
55257              * <li>row - The grid row index</li>
55258              * <li>column - The grid column index</li>
55259              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55260              * </ul>
55261              * @param {Object} e An edit event (see above for description)
55262              */
55263             "validateedit" : true
55264         });
55265     this.on("bodyscroll", this.stopEditing,  this);
55266     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55267 };
55268
55269 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55270     /**
55271      * @cfg {Number} clicksToEdit
55272      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55273      */
55274     clicksToEdit: 2,
55275
55276     // private
55277     isEditor : true,
55278     // private
55279     trackMouseOver: false, // causes very odd FF errors
55280
55281     onCellDblClick : function(g, row, col){
55282         this.startEditing(row, col);
55283     },
55284
55285     onEditComplete : function(ed, value, startValue){
55286         this.editing = false;
55287         this.activeEditor = null;
55288         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55289         var r = ed.record;
55290         var field = this.colModel.getDataIndex(ed.col);
55291         var e = {
55292             grid: this,
55293             record: r,
55294             field: field,
55295             originalValue: startValue,
55296             value: value,
55297             row: ed.row,
55298             column: ed.col,
55299             cancel:false,
55300             editor: ed
55301         };
55302         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55303         cell.show();
55304           
55305         if(String(value) !== String(startValue)){
55306             
55307             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55308                 r.set(field, e.value);
55309                 // if we are dealing with a combo box..
55310                 // then we also set the 'name' colum to be the displayField
55311                 if (ed.field.displayField && ed.field.name) {
55312                     r.set(ed.field.name, ed.field.el.dom.value);
55313                 }
55314                 
55315                 delete e.cancel; //?? why!!!
55316                 this.fireEvent("afteredit", e);
55317             }
55318         } else {
55319             this.fireEvent("afteredit", e); // always fire it!
55320         }
55321         this.view.focusCell(ed.row, ed.col);
55322     },
55323
55324     /**
55325      * Starts editing the specified for the specified row/column
55326      * @param {Number} rowIndex
55327      * @param {Number} colIndex
55328      */
55329     startEditing : function(row, col){
55330         this.stopEditing();
55331         if(this.colModel.isCellEditable(col, row)){
55332             this.view.ensureVisible(row, col, true);
55333           
55334             var r = this.dataSource.getAt(row);
55335             var field = this.colModel.getDataIndex(col);
55336             var cell = Roo.get(this.view.getCell(row,col));
55337             var e = {
55338                 grid: this,
55339                 record: r,
55340                 field: field,
55341                 value: r.data[field],
55342                 row: row,
55343                 column: col,
55344                 cancel:false 
55345             };
55346             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55347                 this.editing = true;
55348                 var ed = this.colModel.getCellEditor(col, row);
55349                 
55350                 if (!ed) {
55351                     return;
55352                 }
55353                 if(!ed.rendered){
55354                     ed.render(ed.parentEl || document.body);
55355                 }
55356                 ed.field.reset();
55357                
55358                 cell.hide();
55359                 
55360                 (function(){ // complex but required for focus issues in safari, ie and opera
55361                     ed.row = row;
55362                     ed.col = col;
55363                     ed.record = r;
55364                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55365                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55366                     this.activeEditor = ed;
55367                     var v = r.data[field];
55368                     ed.startEdit(this.view.getCell(row, col), v);
55369                     // combo's with 'displayField and name set
55370                     if (ed.field.displayField && ed.field.name) {
55371                         ed.field.el.dom.value = r.data[ed.field.name];
55372                     }
55373                     
55374                     
55375                 }).defer(50, this);
55376             }
55377         }
55378     },
55379         
55380     /**
55381      * Stops any active editing
55382      */
55383     stopEditing : function(){
55384         if(this.activeEditor){
55385             this.activeEditor.completeEdit();
55386         }
55387         this.activeEditor = null;
55388     },
55389         
55390          /**
55391      * Called to get grid's drag proxy text, by default returns this.ddText.
55392      * @return {String}
55393      */
55394     getDragDropText : function(){
55395         var count = this.selModel.getSelectedCell() ? 1 : 0;
55396         return String.format(this.ddText, count, count == 1 ? '' : 's');
55397     }
55398         
55399 });/*
55400  * Based on:
55401  * Ext JS Library 1.1.1
55402  * Copyright(c) 2006-2007, Ext JS, LLC.
55403  *
55404  * Originally Released Under LGPL - original licence link has changed is not relivant.
55405  *
55406  * Fork - LGPL
55407  * <script type="text/javascript">
55408  */
55409
55410 // private - not really -- you end up using it !
55411 // This is a support class used internally by the Grid components
55412
55413 /**
55414  * @class Roo.grid.GridEditor
55415  * @extends Roo.Editor
55416  * Class for creating and editable grid elements.
55417  * @param {Object} config any settings (must include field)
55418  */
55419 Roo.grid.GridEditor = function(field, config){
55420     if (!config && field.field) {
55421         config = field;
55422         field = Roo.factory(config.field, Roo.form);
55423     }
55424     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55425     field.monitorTab = false;
55426 };
55427
55428 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55429     
55430     /**
55431      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55432      */
55433     
55434     alignment: "tl-tl",
55435     autoSize: "width",
55436     hideEl : false,
55437     cls: "x-small-editor x-grid-editor",
55438     shim:false,
55439     shadow:"frame"
55440 });/*
55441  * Based on:
55442  * Ext JS Library 1.1.1
55443  * Copyright(c) 2006-2007, Ext JS, LLC.
55444  *
55445  * Originally Released Under LGPL - original licence link has changed is not relivant.
55446  *
55447  * Fork - LGPL
55448  * <script type="text/javascript">
55449  */
55450   
55451
55452   
55453 Roo.grid.PropertyRecord = Roo.data.Record.create([
55454     {name:'name',type:'string'},  'value'
55455 ]);
55456
55457
55458 Roo.grid.PropertyStore = function(grid, source){
55459     this.grid = grid;
55460     this.store = new Roo.data.Store({
55461         recordType : Roo.grid.PropertyRecord
55462     });
55463     this.store.on('update', this.onUpdate,  this);
55464     if(source){
55465         this.setSource(source);
55466     }
55467     Roo.grid.PropertyStore.superclass.constructor.call(this);
55468 };
55469
55470
55471
55472 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55473     setSource : function(o){
55474         this.source = o;
55475         this.store.removeAll();
55476         var data = [];
55477         for(var k in o){
55478             if(this.isEditableValue(o[k])){
55479                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55480             }
55481         }
55482         this.store.loadRecords({records: data}, {}, true);
55483     },
55484
55485     onUpdate : function(ds, record, type){
55486         if(type == Roo.data.Record.EDIT){
55487             var v = record.data['value'];
55488             var oldValue = record.modified['value'];
55489             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55490                 this.source[record.id] = v;
55491                 record.commit();
55492                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55493             }else{
55494                 record.reject();
55495             }
55496         }
55497     },
55498
55499     getProperty : function(row){
55500        return this.store.getAt(row);
55501     },
55502
55503     isEditableValue: function(val){
55504         if(val && val instanceof Date){
55505             return true;
55506         }else if(typeof val == 'object' || typeof val == 'function'){
55507             return false;
55508         }
55509         return true;
55510     },
55511
55512     setValue : function(prop, value){
55513         this.source[prop] = value;
55514         this.store.getById(prop).set('value', value);
55515     },
55516
55517     getSource : function(){
55518         return this.source;
55519     }
55520 });
55521
55522 Roo.grid.PropertyColumnModel = function(grid, store){
55523     this.grid = grid;
55524     var g = Roo.grid;
55525     g.PropertyColumnModel.superclass.constructor.call(this, [
55526         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55527         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55528     ]);
55529     this.store = store;
55530     this.bselect = Roo.DomHelper.append(document.body, {
55531         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55532             {tag: 'option', value: 'true', html: 'true'},
55533             {tag: 'option', value: 'false', html: 'false'}
55534         ]
55535     });
55536     Roo.id(this.bselect);
55537     var f = Roo.form;
55538     this.editors = {
55539         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55540         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55541         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55542         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55543         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55544     };
55545     this.renderCellDelegate = this.renderCell.createDelegate(this);
55546     this.renderPropDelegate = this.renderProp.createDelegate(this);
55547 };
55548
55549 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55550     
55551     
55552     nameText : 'Name',
55553     valueText : 'Value',
55554     
55555     dateFormat : 'm/j/Y',
55556     
55557     
55558     renderDate : function(dateVal){
55559         return dateVal.dateFormat(this.dateFormat);
55560     },
55561
55562     renderBool : function(bVal){
55563         return bVal ? 'true' : 'false';
55564     },
55565
55566     isCellEditable : function(colIndex, rowIndex){
55567         return colIndex == 1;
55568     },
55569
55570     getRenderer : function(col){
55571         return col == 1 ?
55572             this.renderCellDelegate : this.renderPropDelegate;
55573     },
55574
55575     renderProp : function(v){
55576         return this.getPropertyName(v);
55577     },
55578
55579     renderCell : function(val){
55580         var rv = val;
55581         if(val instanceof Date){
55582             rv = this.renderDate(val);
55583         }else if(typeof val == 'boolean'){
55584             rv = this.renderBool(val);
55585         }
55586         return Roo.util.Format.htmlEncode(rv);
55587     },
55588
55589     getPropertyName : function(name){
55590         var pn = this.grid.propertyNames;
55591         return pn && pn[name] ? pn[name] : name;
55592     },
55593
55594     getCellEditor : function(colIndex, rowIndex){
55595         var p = this.store.getProperty(rowIndex);
55596         var n = p.data['name'], val = p.data['value'];
55597         
55598         if(typeof(this.grid.customEditors[n]) == 'string'){
55599             return this.editors[this.grid.customEditors[n]];
55600         }
55601         if(typeof(this.grid.customEditors[n]) != 'undefined'){
55602             return this.grid.customEditors[n];
55603         }
55604         if(val instanceof Date){
55605             return this.editors['date'];
55606         }else if(typeof val == 'number'){
55607             return this.editors['number'];
55608         }else if(typeof val == 'boolean'){
55609             return this.editors['boolean'];
55610         }else{
55611             return this.editors['string'];
55612         }
55613     }
55614 });
55615
55616 /**
55617  * @class Roo.grid.PropertyGrid
55618  * @extends Roo.grid.EditorGrid
55619  * This class represents the  interface of a component based property grid control.
55620  * <br><br>Usage:<pre><code>
55621  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55622       
55623  });
55624  // set any options
55625  grid.render();
55626  * </code></pre>
55627   
55628  * @constructor
55629  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55630  * The container MUST have some type of size defined for the grid to fill. The container will be
55631  * automatically set to position relative if it isn't already.
55632  * @param {Object} config A config object that sets properties on this grid.
55633  */
55634 Roo.grid.PropertyGrid = function(container, config){
55635     config = config || {};
55636     var store = new Roo.grid.PropertyStore(this);
55637     this.store = store;
55638     var cm = new Roo.grid.PropertyColumnModel(this, store);
55639     store.store.sort('name', 'ASC');
55640     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55641         ds: store.store,
55642         cm: cm,
55643         enableColLock:false,
55644         enableColumnMove:false,
55645         stripeRows:false,
55646         trackMouseOver: false,
55647         clicksToEdit:1
55648     }, config));
55649     this.getGridEl().addClass('x-props-grid');
55650     this.lastEditRow = null;
55651     this.on('columnresize', this.onColumnResize, this);
55652     this.addEvents({
55653          /**
55654              * @event beforepropertychange
55655              * Fires before a property changes (return false to stop?)
55656              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55657              * @param {String} id Record Id
55658              * @param {String} newval New Value
55659          * @param {String} oldval Old Value
55660              */
55661         "beforepropertychange": true,
55662         /**
55663              * @event propertychange
55664              * Fires after a property changes
55665              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55666              * @param {String} id Record Id
55667              * @param {String} newval New Value
55668          * @param {String} oldval Old Value
55669              */
55670         "propertychange": true
55671     });
55672     this.customEditors = this.customEditors || {};
55673 };
55674 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55675     
55676      /**
55677      * @cfg {Object} customEditors map of colnames=> custom editors.
55678      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55679      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55680      * false disables editing of the field.
55681          */
55682     
55683       /**
55684      * @cfg {Object} propertyNames map of property Names to their displayed value
55685          */
55686     
55687     render : function(){
55688         Roo.grid.PropertyGrid.superclass.render.call(this);
55689         this.autoSize.defer(100, this);
55690     },
55691
55692     autoSize : function(){
55693         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55694         if(this.view){
55695             this.view.fitColumns();
55696         }
55697     },
55698
55699     onColumnResize : function(){
55700         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55701         this.autoSize();
55702     },
55703     /**
55704      * Sets the data for the Grid
55705      * accepts a Key => Value object of all the elements avaiable.
55706      * @param {Object} data  to appear in grid.
55707      */
55708     setSource : function(source){
55709         this.store.setSource(source);
55710         //this.autoSize();
55711     },
55712     /**
55713      * Gets all the data from the grid.
55714      * @return {Object} data  data stored in grid
55715      */
55716     getSource : function(){
55717         return this.store.getSource();
55718     }
55719 });/*
55720  * Based on:
55721  * Ext JS Library 1.1.1
55722  * Copyright(c) 2006-2007, Ext JS, LLC.
55723  *
55724  * Originally Released Under LGPL - original licence link has changed is not relivant.
55725  *
55726  * Fork - LGPL
55727  * <script type="text/javascript">
55728  */
55729  
55730 /**
55731  * @class Roo.LoadMask
55732  * A simple utility class for generically masking elements while loading data.  If the element being masked has
55733  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
55734  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
55735  * element's UpdateManager load indicator and will be destroyed after the initial load.
55736  * @constructor
55737  * Create a new LoadMask
55738  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
55739  * @param {Object} config The config object
55740  */
55741 Roo.LoadMask = function(el, config){
55742     this.el = Roo.get(el);
55743     Roo.apply(this, config);
55744     if(this.store){
55745         this.store.on('beforeload', this.onBeforeLoad, this);
55746         this.store.on('load', this.onLoad, this);
55747         this.store.on('loadexception', this.onLoadException, this);
55748         this.removeMask = false;
55749     }else{
55750         var um = this.el.getUpdateManager();
55751         um.showLoadIndicator = false; // disable the default indicator
55752         um.on('beforeupdate', this.onBeforeLoad, this);
55753         um.on('update', this.onLoad, this);
55754         um.on('failure', this.onLoad, this);
55755         this.removeMask = true;
55756     }
55757 };
55758
55759 Roo.LoadMask.prototype = {
55760     /**
55761      * @cfg {Boolean} removeMask
55762      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
55763      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
55764      */
55765     /**
55766      * @cfg {String} msg
55767      * The text to display in a centered loading message box (defaults to 'Loading...')
55768      */
55769     msg : 'Loading...',
55770     /**
55771      * @cfg {String} msgCls
55772      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
55773      */
55774     msgCls : 'x-mask-loading',
55775
55776     /**
55777      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
55778      * @type Boolean
55779      */
55780     disabled: false,
55781
55782     /**
55783      * Disables the mask to prevent it from being displayed
55784      */
55785     disable : function(){
55786        this.disabled = true;
55787     },
55788
55789     /**
55790      * Enables the mask so that it can be displayed
55791      */
55792     enable : function(){
55793         this.disabled = false;
55794     },
55795     
55796     onLoadException : function()
55797     {
55798         Roo.log(arguments);
55799         
55800         if (typeof(arguments[3]) != 'undefined') {
55801             Roo.MessageBox.alert("Error loading",arguments[3]);
55802         } 
55803         /*
55804         try {
55805             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55806                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55807             }   
55808         } catch(e) {
55809             
55810         }
55811         */
55812     
55813         
55814         
55815         this.el.unmask(this.removeMask);
55816     },
55817     // private
55818     onLoad : function()
55819     {
55820         this.el.unmask(this.removeMask);
55821     },
55822
55823     // private
55824     onBeforeLoad : function(){
55825         if(!this.disabled){
55826             this.el.mask(this.msg, this.msgCls);
55827         }
55828     },
55829
55830     // private
55831     destroy : function(){
55832         if(this.store){
55833             this.store.un('beforeload', this.onBeforeLoad, this);
55834             this.store.un('load', this.onLoad, this);
55835             this.store.un('loadexception', this.onLoadException, this);
55836         }else{
55837             var um = this.el.getUpdateManager();
55838             um.un('beforeupdate', this.onBeforeLoad, this);
55839             um.un('update', this.onLoad, this);
55840             um.un('failure', this.onLoad, this);
55841         }
55842     }
55843 };/*
55844  * Based on:
55845  * Ext JS Library 1.1.1
55846  * Copyright(c) 2006-2007, Ext JS, LLC.
55847  *
55848  * Originally Released Under LGPL - original licence link has changed is not relivant.
55849  *
55850  * Fork - LGPL
55851  * <script type="text/javascript">
55852  */
55853
55854
55855 /**
55856  * @class Roo.XTemplate
55857  * @extends Roo.Template
55858  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
55859 <pre><code>
55860 var t = new Roo.XTemplate(
55861         '&lt;select name="{name}"&gt;',
55862                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
55863         '&lt;/select&gt;'
55864 );
55865  
55866 // then append, applying the master template values
55867  </code></pre>
55868  *
55869  * Supported features:
55870  *
55871  *  Tags:
55872
55873 <pre><code>
55874       {a_variable} - output encoded.
55875       {a_variable.format:("Y-m-d")} - call a method on the variable
55876       {a_variable:raw} - unencoded output
55877       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
55878       {a_variable:this.method_on_template(...)} - call a method on the template object.
55879  
55880 </code></pre>
55881  *  The tpl tag:
55882 <pre><code>
55883         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
55884         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
55885         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
55886         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
55887   
55888         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
55889         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
55890 </code></pre>
55891  *      
55892  */
55893 Roo.XTemplate = function()
55894 {
55895     Roo.XTemplate.superclass.constructor.apply(this, arguments);
55896     if (this.html) {
55897         this.compile();
55898     }
55899 };
55900
55901
55902 Roo.extend(Roo.XTemplate, Roo.Template, {
55903
55904     /**
55905      * The various sub templates
55906      */
55907     tpls : false,
55908     /**
55909      *
55910      * basic tag replacing syntax
55911      * WORD:WORD()
55912      *
55913      * // you can fake an object call by doing this
55914      *  x.t:(test,tesT) 
55915      * 
55916      */
55917     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
55918
55919     /**
55920      * compile the template
55921      *
55922      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
55923      *
55924      */
55925     compile: function()
55926     {
55927         var s = this.html;
55928      
55929         s = ['<tpl>', s, '</tpl>'].join('');
55930     
55931         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
55932             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
55933             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
55934             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
55935             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
55936             m,
55937             id     = 0,
55938             tpls   = [];
55939     
55940         while(true == !!(m = s.match(re))){
55941             var forMatch   = m[0].match(nameRe),
55942                 ifMatch   = m[0].match(ifRe),
55943                 execMatch   = m[0].match(execRe),
55944                 namedMatch   = m[0].match(namedRe),
55945                 
55946                 exp  = null, 
55947                 fn   = null,
55948                 exec = null,
55949                 name = forMatch && forMatch[1] ? forMatch[1] : '';
55950                 
55951             if (ifMatch) {
55952                 // if - puts fn into test..
55953                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
55954                 if(exp){
55955                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
55956                 }
55957             }
55958             
55959             if (execMatch) {
55960                 // exec - calls a function... returns empty if true is  returned.
55961                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
55962                 if(exp){
55963                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
55964                 }
55965             }
55966             
55967             
55968             if (name) {
55969                 // for = 
55970                 switch(name){
55971                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
55972                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
55973                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
55974                 }
55975             }
55976             var uid = namedMatch ? namedMatch[1] : id;
55977             
55978             
55979             tpls.push({
55980                 id:     namedMatch ? namedMatch[1] : id,
55981                 target: name,
55982                 exec:   exec,
55983                 test:   fn,
55984                 body:   m[1] || ''
55985             });
55986             if (namedMatch) {
55987                 s = s.replace(m[0], '');
55988             } else { 
55989                 s = s.replace(m[0], '{xtpl'+ id + '}');
55990             }
55991             ++id;
55992         }
55993         this.tpls = [];
55994         for(var i = tpls.length-1; i >= 0; --i){
55995             this.compileTpl(tpls[i]);
55996             this.tpls[tpls[i].id] = tpls[i];
55997         }
55998         this.master = tpls[tpls.length-1];
55999         return this;
56000     },
56001     /**
56002      * same as applyTemplate, except it's done to one of the subTemplates
56003      * when using named templates, you can do:
56004      *
56005      * var str = pl.applySubTemplate('your-name', values);
56006      *
56007      * 
56008      * @param {Number} id of the template
56009      * @param {Object} values to apply to template
56010      * @param {Object} parent (normaly the instance of this object)
56011      */
56012     applySubTemplate : function(id, values, parent)
56013     {
56014         
56015         
56016         var t = this.tpls[id];
56017         
56018         
56019         try { 
56020             if(t.test && !t.test.call(this, values, parent)){
56021                 return '';
56022             }
56023         } catch(e) {
56024             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
56025             Roo.log(e.toString());
56026             Roo.log(t.test);
56027             return ''
56028         }
56029         try { 
56030             
56031             if(t.exec && t.exec.call(this, values, parent)){
56032                 return '';
56033             }
56034         } catch(e) {
56035             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
56036             Roo.log(e.toString());
56037             Roo.log(t.exec);
56038             return ''
56039         }
56040         try {
56041             var vs = t.target ? t.target.call(this, values, parent) : values;
56042             parent = t.target ? values : parent;
56043             if(t.target && vs instanceof Array){
56044                 var buf = [];
56045                 for(var i = 0, len = vs.length; i < len; i++){
56046                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
56047                 }
56048                 return buf.join('');
56049             }
56050             return t.compiled.call(this, vs, parent);
56051         } catch (e) {
56052             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
56053             Roo.log(e.toString());
56054             Roo.log(t.compiled);
56055             return '';
56056         }
56057     },
56058
56059     compileTpl : function(tpl)
56060     {
56061         var fm = Roo.util.Format;
56062         var useF = this.disableFormats !== true;
56063         var sep = Roo.isGecko ? "+" : ",";
56064         var undef = function(str) {
56065             Roo.log("Property not found :"  + str);
56066             return '';
56067         };
56068         
56069         var fn = function(m, name, format, args)
56070         {
56071             //Roo.log(arguments);
56072             args = args ? args.replace(/\\'/g,"'") : args;
56073             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
56074             if (typeof(format) == 'undefined') {
56075                 format= 'htmlEncode';
56076             }
56077             if (format == 'raw' ) {
56078                 format = false;
56079             }
56080             
56081             if(name.substr(0, 4) == 'xtpl'){
56082                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
56083             }
56084             
56085             // build an array of options to determine if value is undefined..
56086             
56087             // basically get 'xxxx.yyyy' then do
56088             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
56089             //    (function () { Roo.log("Property not found"); return ''; })() :
56090             //    ......
56091             
56092             var udef_ar = [];
56093             var lookfor = '';
56094             Roo.each(name.split('.'), function(st) {
56095                 lookfor += (lookfor.length ? '.': '') + st;
56096                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
56097             });
56098             
56099             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
56100             
56101             
56102             if(format && useF){
56103                 
56104                 args = args ? ',' + args : "";
56105                  
56106                 if(format.substr(0, 5) != "this."){
56107                     format = "fm." + format + '(';
56108                 }else{
56109                     format = 'this.call("'+ format.substr(5) + '", ';
56110                     args = ", values";
56111                 }
56112                 
56113                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
56114             }
56115              
56116             if (args.length) {
56117                 // called with xxyx.yuu:(test,test)
56118                 // change to ()
56119                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
56120             }
56121             // raw.. - :raw modifier..
56122             return "'"+ sep + udef_st  + name + ")"+sep+"'";
56123             
56124         };
56125         var body;
56126         // branched to use + in gecko and [].join() in others
56127         if(Roo.isGecko){
56128             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
56129                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
56130                     "';};};";
56131         }else{
56132             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
56133             body.push(tpl.body.replace(/(\r\n|\n)/g,
56134                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
56135             body.push("'].join('');};};");
56136             body = body.join('');
56137         }
56138         
56139         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
56140        
56141         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
56142         eval(body);
56143         
56144         return this;
56145     },
56146
56147     applyTemplate : function(values){
56148         return this.master.compiled.call(this, values, {});
56149         //var s = this.subs;
56150     },
56151
56152     apply : function(){
56153         return this.applyTemplate.apply(this, arguments);
56154     }
56155
56156  });
56157
56158 Roo.XTemplate.from = function(el){
56159     el = Roo.getDom(el);
56160     return new Roo.XTemplate(el.value || el.innerHTML);
56161 };